diff --git a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java index 1afd536a6c..441dc9c06b 100644 --- a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java +++ b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/Config.java @@ -29,10 +29,10 @@ @AttributeDefinition(name = "Recharge power", description = "If grid purchase power is below this value battery is recharged.") int rechargePower(); - @AttributeDefinition(name = "Enable multiple ess constraints", description = "A fixed capacity configured by the minSocLimit is used for peakshaving. Additional capacity could be used by other controllers.") - boolean enableMultipleEssConstraints() default false; + @AttributeDefinition(name = "Allow other use cases in a parallel multi use setup", description = "The peak shaving controller uses a fixed capacity configured by the minSocLimit. Other controllers can use the remaining capacity.") + boolean allowParallelMultiUse() default false; - @AttributeDefinition(name = "Minimum SoC required for Peak Shaving", description = "The controller force charges with the available surpluss till this SOC limit") + @AttributeDefinition(name = "Minimum SoC required for Peak Shaving", description = "The controller force charges with the available surpluss till this SOC limit.") int minSocLimit() default 70; @AttributeDefinition(name = "SoC hysterersis", description = "SoC hysteresis to avoid switching between force and soft limitation") diff --git a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShaving.java b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShaving.java index c36032d214..1aeda12498 100644 --- a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShaving.java +++ b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShaving.java @@ -1,12 +1,18 @@ package io.openems.edge.controller.symmetric.peakshaving; import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.channel.StateChannel; +import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.controller.api.Controller; +import io.openems.edge.controller.api.Controller.ChannelId; public interface ControllerEssPeakShaving extends Controller, OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + MULTI_USE_STATE(Doc.of(MultiUseState.values()) // + .text("The current state if multi use is allowed")); + ; private final Doc doc; @@ -19,5 +25,24 @@ public Doc doc() { return this.doc; } } + + /** + * Gets the Channel for {@link ChannelId#RUN_FAILED}. + * + * @return the Channel + */ + public default StateChannel getMultiUseStateChannel() { + return this.channel(ChannelId.MULTI_USE_STATE); + } + + /** + * Internal method to set the 'nextValue' on {@link ChannelId#RUN_FAILED} + * Channel. + * + * @param value the next value + */ + public default void setMultiUseState(MultiUseState state) { + this.getRunFailedChannel().setNextValue(state); + } } diff --git a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImpl.java b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImpl.java index bd5198daeb..d02c200ce7 100644 --- a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImpl.java +++ b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImpl.java @@ -1,5 +1,7 @@ package io.openems.edge.controller.symmetric.peakshaving; +import java.util.Optional; + import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -16,6 +18,10 @@ import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.controller.api.Controller; import io.openems.edge.ess.api.ManagedSymmetricEss; +import io.openems.edge.ess.api.PowerConstraint; +import io.openems.edge.ess.power.api.Phase; +import io.openems.edge.ess.power.api.Pwr; +import io.openems.edge.ess.power.api.Relationship; import io.openems.edge.meter.api.ElectricityMeter; @Designate(ocd = Config.class, factory = true) @@ -36,7 +42,7 @@ public class ControllerEssPeakShavingImpl extends AbstractOpenemsComponent private Config config; - private BehaviourState previousBehaviour = BehaviourState.FIXED_LIMITATION; + private MultiUseState multiUseBehavior = MultiUseState.NONE; public ControllerEssPeakShavingImpl() { super(// @@ -80,76 +86,48 @@ public void run() throws OpenemsNamedException { var soc = ess.getSoc().orElse(0); - var behaviour = BehaviourState.FIXED_LIMITATION; - if (this.config.enableMultipleEssConstraints()) { + if (this.config.allowParallelMultiUse()) { - behaviour = getSocSubstate(soc, this.config.minSocLimit(), this.config.socHysteresis(), - this.previousBehaviour); - this.previousBehaviour = behaviour; + this.multiUseBehavior = this.multiUseBehavior.getMultiUseBehavior(soc, this.config.minSocLimit(), + this.config.socHysteresis()); } // Calculate 'real' grid-power (without current ESS charge/discharge) var gridPower = meter.getActivePower().getOrError() /* current buy-from/sell-to grid */ + ess.getActivePower().getOrError() /* current charge/discharge Ess */; - int calculatedPower; + Optional peakShaveCompensationPower = Optional.empty(); if (gridPower >= this.config.peakShavingPower()) { - /* - * Peak-Shaving - */ - calculatedPower = gridPower - this.config.peakShavingPower(); - - } else if (gridPower <= this.config.rechargePower()) { - /* - * Recharge - */ - calculatedPower = gridPower - this.config.rechargePower(); - - } else { - /* - * Do nothing - */ - calculatedPower = 0; + // we've to do peak shaving + peakShaveCompensationPower = Optional.of(gridPower - this.config.peakShavingPower()); } - - switch (behaviour) { - case FIXED_LIMITATION -> { - - /* - * set result - */ - ess.setActivePowerEqualsWithPid(calculatedPower); + + this.setMultiUseState(this.multiUseBehavior); + switch (this.multiUseBehavior) { + case NONE -> { + ess.setActivePowerEqualsWithPid(peakShaveCompensationPower.orElseGet(() -> { + // If peak shaving is not required, recharge has to happen. + return gridPower - this.config.rechargePower(); + })); + ess.setReactivePowerEquals(0); } - case SOFT_LIMITATION -> { - ess.setActivePowerGreaterOrEquals(calculatedPower); - } - } - - ess.setReactivePowerEquals(0); - } - - protected static enum BehaviourState { - FIXED_LIMITATION, // - SOFT_LIMITATION; - } - - protected static BehaviourState getSocSubstate(int soc, int minSoc, int socBuffer, - BehaviourState previousBehaviour) { - - return switch (previousBehaviour) { - case FIXED_LIMITATION -> { - if (soc >= minSoc + socBuffer) { - yield BehaviourState.SOFT_LIMITATION; + case PARALLEL -> { + // If peak shaving does not happen do nothing, and allow follow up controllers + // to set any value. If peak shaving has to happen don't allow other controllers + // to do anything to guarantee quick reaction of battery. + if (peakShaveCompensationPower.isPresent()) { + ess.setActivePowerGreaterOrEquals(peakShaveCompensationPower.get()); + ess.setReactivePowerEquals(0); + } else { + // one can utilize the battery within the boundaries configured for this + // controller + log.info("Running in parallel mode that allows everything"); + PowerConstraint.apply(ess, "Parallel multi use constraints of peak shaving", Phase.ALL, Pwr.ACTIVE, + Relationship.GREATER_OR_EQUALS, -this.config.rechargePower()); } - yield BehaviourState.FIXED_LIMITATION; } - case SOFT_LIMITATION -> { - if (soc <= minSoc) { - yield BehaviourState.FIXED_LIMITATION; - } - yield BehaviourState.SOFT_LIMITATION; } - }; + } } diff --git a/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/MultiUseState.java b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/MultiUseState.java new file mode 100644 index 0000000000..b4966915e4 --- /dev/null +++ b/io.openems.edge.controller.symmetric.peakshaving/src/io/openems/edge/controller/symmetric/peakshaving/MultiUseState.java @@ -0,0 +1,68 @@ +package io.openems.edge.controller.symmetric.peakshaving; + +import io.openems.common.types.OptionsEnum; + +enum MultiUseState implements OptionsEnum { + /** + * If no multi use is active, this controller will set a fixed value for active + * power, which no follow up controller can override. + */ + NONE(0, "No multi use is currently allowed"), + /** + * If parallel multi use is active, this controller sets a minimum value, which + * allows follow up controllers to override another value, which applies to the + * given constraints. + */ + PARALLEL(1, "SoC based parallel multi use is allowed"); + + private final int value; + private final String name; + + private MultiUseState(int value, String name) { + this.value = value; + this.name = name; + } + + @Override + public int getValue() { + return this.value; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public OptionsEnum getUndefined() { + return NONE; + } + + /** + * Returns the desired multi use behavior based on the input values. + * + * @param soc The current state of charge. + * @param minSoc The required state of charge for this peak shaving multi use + * case. + * @param socBuffer The hysteresis buffer to add on top of the SoC before + * allowing parallel multi use. + * @return The new multi use state. + */ + public MultiUseState getMultiUseBehavior(int soc, int minSoc, int socBuffer) { + + return switch (this) { + case NONE -> { + if (soc >= minSoc + socBuffer) { + yield MultiUseState.PARALLEL; + } + yield MultiUseState.NONE; + } + case PARALLEL -> { + if (soc <= minSoc) { + yield MultiUseState.NONE; + } + yield MultiUseState.PARALLEL; + } + }; + } +} \ No newline at end of file diff --git a/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImplTest.java b/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImplTest.java index f51e3ea6e9..e2912a92e1 100644 --- a/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImplTest.java +++ b/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/ControllerEssPeakShavingImplTest.java @@ -2,11 +2,13 @@ import org.junit.Test; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.ChannelAddress; import io.openems.edge.common.test.AbstractComponentTest.TestCase; import io.openems.edge.common.test.DummyComponentManager; import io.openems.edge.controller.test.ControllerTest; import io.openems.edge.ess.api.ManagedSymmetricEss; +import io.openems.edge.ess.api.PowerConstraint; import io.openems.edge.ess.api.SymmetricEss; import io.openems.edge.ess.test.DummyManagedSymmetricEss; import io.openems.edge.ess.test.DummyPower; @@ -41,14 +43,14 @@ public void test() throws Exception { .setMeterId(METER_ID) // .setPeakShavingPower(100_000) // .setRechargePower(50_000) // - .setEnableMultipleEssConstraints(false)// + .setAllowParallelMultiUse(false) // .setMinSocLimit(50) // .setSocHysteresis(3) // .build()) .next(new TestCase() // .input(ESS_ACTIVE_POWER, 0) // .input(METER_ACTIVE_POWER, 120000) // - .input(ESS_ACTIVE_POWER, 0)// + .input(ESS_ACTIVE_POWER, 0) // .output(ESS_SET_ACTIVE_POWER_EQUALS, 6000)) // .next(new TestCase() // .input(ESS_ACTIVE_POWER, 0) // @@ -105,110 +107,101 @@ public void test() throws Exception { } @Test - public void test_soft_limit() throws Exception { + public void test_parallel_multi_use() throws Exception { + int peakShavingPowerThreshold = 50_000; + int maxDischargePower = peakShavingPowerThreshold; + DummyManagedSymmetricEss dummyEss = new DummyManagedSymmetricEss(ESS_ID) // + .withAllowedDischargePower(maxDischargePower); + int maxRechargePower = 30_000; new ControllerTest(new ControllerEssPeakShavingImpl()) // .addReference("componentManager", new DummyComponentManager()) // - .addComponent(new DummyManagedSymmetricEss(ESS_ID)) // + .addComponent(dummyEss) // .addComponent(new DummyElectricityMeter(METER_ID)) // .activate(MyConfig.create() // .setId(CTRL_ID) // .setEssId(ESS_ID) // .setMeterId(METER_ID) // - .setPeakShavingPower(50_000) // - .setRechargePower(30_000) // - .setEnableMultipleEssConstraints(true)// - .setMinSocLimit(50)// - .setSocHysteresis(3)// - .build())// - .next(new TestCase() // + .setPeakShavingPower(peakShavingPowerThreshold) // + .setRechargePower(maxRechargePower) // + .setAllowParallelMultiUse(true) // + .setMinSocLimit(50) // + .setSocHysteresis(3) // + .build()) // + .next(new TestCase("Don't allow parallel multi use, because soc is far below threshold") // .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_ACTIVE_POWER, 0)// - .input(ESS_SOC, 40)// - .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) // - .next(new TestCase() // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 50)// + .input(ESS_SOC, 40) // .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) // - .next(new TestCase() // + .next(new TestCase("Don't allow parallel multi use, because soc matches the threshold") // .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 51)// + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 50) // .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) // - .next(new TestCase() // + .next(new TestCase("Don't allow parallel multi use, because soc is below threshold (incl. hysteresis buffer)") // .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 52)// + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 51) // .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) // - .next(new TestCase() // + .next(new TestCase("Don't allow parallel multi use, because soc is below threshold (incl. hysteresis buffer)") // .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 53)// + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 52) // .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) // - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 54)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) // - .next(new TestCase("Above peak shaving power") // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 54)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) // - .next(new TestCase("within boundaries") // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 40_000) // - .input(ESS_SOC, 54)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 0)) // - .next(new TestCase("below recharge power") // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 20_000) // - .input(ESS_SOC, 54)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, -10_000)) - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 54)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 53)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 52)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 52)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) - - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 51)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 51)// - .output(ESS_SET_ACTIVE_POWER_GREATER_THAN_EQUALS, 10_000)) - - .next(new TestCase() // - .input(ESS_ACTIVE_POWER, 0) // - .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 50)// - .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) - - .next(new TestCase() // + .next(new TestCase("Allow parallel multi use, because soc matches threshold (incl. hysteresis buffer)") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 53) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 54) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold. Don't allow charging as it would exceed peak shaving threshold.") // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold - 10_000) // + .input(ESS_SOC, 54) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold. Allow charging with 20kW which still respects peak shaving threshold.") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold - 20_000) // + .input(ESS_SOC, 54) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 54) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold (incl. hysteresis buffer)") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 53) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold (incl. hysteresis buffer)") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 52) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold (incl. hysteresis buffer)") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 52) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Allow multi use, because soc is above threshold (incl. hysteresis buffer)") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 51) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, null)) // + .next(new TestCase("Don't allow parallel multi use, because soc matches the threshold") // + .input(ESS_ACTIVE_POWER, 0) // + .input(METER_ACTIVE_POWER, peakShavingPowerThreshold + 10_000) // + .input(ESS_SOC, 50) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) // + .next(new TestCase("Don't allow parallel multi use, because soc is below the threshold") // .input(ESS_ACTIVE_POWER, 0) // .input(METER_ACTIVE_POWER, 60_000) // - .input(ESS_SOC, 49)// - .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) + .input(ESS_SOC, 49) // + .output(ESS_SET_ACTIVE_POWER_EQUALS, 10_000)) // ; // } } diff --git a/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/MultiUseStateTest.java b/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/MultiUseStateTest.java new file mode 100644 index 0000000000..f81a3dde34 --- /dev/null +++ b/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/MultiUseStateTest.java @@ -0,0 +1,32 @@ +package io.openems.edge.controller.symmetric.peakshaving; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class MultiUseStateTest { + + @Test + public void testCorrectTransitionsFromNone() { + assertEquals(MultiUseState.NONE, MultiUseState.NONE.getMultiUseBehavior(60, 80, 0)); + assertEquals(MultiUseState.NONE, MultiUseState.NONE.getMultiUseBehavior(79, 80, 0)); + assertEquals(MultiUseState.NONE, MultiUseState.NONE.getMultiUseBehavior(79, 78, 2)); + + assertEquals(MultiUseState.PARALLEL, MultiUseState.NONE.getMultiUseBehavior(80, 80, 0)); + assertEquals(MultiUseState.PARALLEL, MultiUseState.NONE.getMultiUseBehavior(81, 80, 0)); + assertEquals(MultiUseState.PARALLEL, MultiUseState.NONE.getMultiUseBehavior(81, 78, 2)); + assertEquals(MultiUseState.PARALLEL, MultiUseState.NONE.getMultiUseBehavior(90, 80, 0)); + } + + @Test + public void testCorrectTransitionsFromParallel() { + assertEquals(MultiUseState.NONE, MultiUseState.PARALLEL.getMultiUseBehavior(60, 80, 0)); + assertEquals(MultiUseState.NONE, MultiUseState.PARALLEL.getMultiUseBehavior(79, 80, 0)); + assertEquals(MultiUseState.NONE, MultiUseState.PARALLEL.getMultiUseBehavior(80, 80, 0)); + + assertEquals(MultiUseState.PARALLEL, MultiUseState.PARALLEL.getMultiUseBehavior(80, 78, 2)); + assertEquals(MultiUseState.PARALLEL, MultiUseState.PARALLEL.getMultiUseBehavior(79, 78, 2)); + assertEquals(MultiUseState.PARALLEL, MultiUseState.PARALLEL.getMultiUseBehavior(80, 78, 2)); + assertEquals(MultiUseState.PARALLEL, MultiUseState.PARALLEL.getMultiUseBehavior(90, 80, 0)); + } +} diff --git a/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/MyConfig.java b/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/MyConfig.java index 65725d0c55..e94deff35f 100644 --- a/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/MyConfig.java +++ b/io.openems.edge.controller.symmetric.peakshaving/test/io/openems/edge/controller/symmetric/peakshaving/MyConfig.java @@ -12,7 +12,7 @@ protected static class Builder { private int peakShavingPower; private int rechargePower; private int minSocLimit; - private boolean enableMultipleEssConstraints; + private boolean allowParallelMultiUse; private int socHysteresis; private Builder() { @@ -48,8 +48,8 @@ public Builder setMinSocLimit(int minSocLimit) { return this; } - public Builder setEnableMultipleEssConstraints(boolean enableMultipleEssConstraints) { - this.enableMultipleEssConstraints = enableMultipleEssConstraints; + public Builder setAllowParallelMultiUse(boolean allowParallelMultiUse) { + this.allowParallelMultiUse = allowParallelMultiUse; return this; } @@ -105,8 +105,8 @@ public int minSocLimit() { } @Override - public boolean enableMultipleEssConstraints() { - return this.builder.enableMultipleEssConstraints; + public boolean allowParallelMultiUse() { + return this.builder.allowParallelMultiUse; } @Override