getRestrictionModeChannel() {
- return this.channel(ChannelId.RESTRICTION_MODE);
- }
-
- /**
- * Gets the restriction mode. See {@link ChannelId#RESTRICTION_MODE}.
- *
- * @return the Channel {@link Value}
- */
- default EssRestrictionLevel getRestrictionMode() {
- return this.getRestrictionModeChannel().value().asEnum();
- }
-
- /**
- * Sets the restriction mode. See {@link ChannelId#RESTRICTION_MODE}.
- *
- * @param value the value as {@link EssRestrictionLevel}.
- */
- default void _setRestrictionMode(EssRestrictionLevel value) {
- this.getRestrictionModeChannel().setNextValue(value);
- }
-
- /**
- * Represents the current {@link EssRestrictionLevel}.
- *
- *
- * This is determined by the external ripple control signals.
- *
- *
- * @return the current restriction level.
- */
- EssRestrictionLevel essRestrictionLevel();
-
- /**
- * Calculates the currently allowed grid feed-in power. This is determined as
- * the minimum of:
- *
- * - the maximum apparent power multiplied by the limitation factor
- * - the raw limitation value
- *
- *
- * @param maxApparentPower the maximum apparent power in VA.
- * @return the allowed grid feed-in power in VA.
- */
- default int getDynamicGridFeedInLimit(int maxApparentPower) {
- return (int) (maxApparentPower * this.essRestrictionLevel().getLimitationFactor());
- }
-}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/PowerProductionLimiter.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/PowerProductionLimiter.java
new file mode 100644
index 00000000000..208ea96d2f4
--- /dev/null
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/PowerProductionLimiter.java
@@ -0,0 +1,12 @@
+package io.openems.edge.controller.ess.ripplecontrolreceiver;
+
+public interface PowerProductionLimiter {
+ void setMaxNominalProductionPower(int maxNominalProductionPowerInW);
+
+ /**
+ * Returns how much watt we can feed into the grid.
+ *
+ * @return Limit in Wh
+ */
+ Integer getGridFeedInLimit();
+}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/PowerProductionLimiterComponent.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/PowerProductionLimiterComponent.java
new file mode 100644
index 00000000000..7f175d626e9
--- /dev/null
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/PowerProductionLimiterComponent.java
@@ -0,0 +1,37 @@
+package io.openems.edge.controller.ess.ripplecontrolreceiver;
+
+import io.openems.common.channel.Unit;
+import io.openems.edge.common.channel.Doc;
+import io.openems.edge.common.component.OpenemsComponent;
+
+import static io.openems.common.channel.PersistencePriority.HIGH;
+import static io.openems.common.types.OpenemsType.INTEGER;
+import static io.openems.common.types.OpenemsType.LONG;
+
+public interface PowerProductionLimiterComponent extends PowerProductionLimiter, OpenemsComponent {
+ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
+
+ RESTRICTION(Doc.of(INTEGER)
+ .unit(Unit.WATT)
+ .persistencePriority(HIGH)), //
+
+ CUMULATED_RESTRICTION_TIME(Doc.of(LONG)//
+ .unit(Unit.CUMULATED_SECONDS)//
+ .persistencePriority(HIGH)); //
+
+ private final Doc doc;
+
+ ChannelId(Doc doc) {
+ this.doc = doc;
+ }
+
+ @Override
+ public Doc doc() {
+ return this.doc;
+ }
+ }
+
+ public default Integer getGridFeedInLimit() {
+ return (Integer) this.channel(ChannelId.RESTRICTION).value().orElse(null);
+ }
+}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/Config.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/Config.java
new file mode 100644
index 00000000000..8e5e2ff58e3
--- /dev/null
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/Config.java
@@ -0,0 +1,25 @@
+package io.openems.edge.controller.ess.ripplecontrolreceiver.eebus;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+@ObjectClassDefinition(//
+ name = "Controller Ess Ripple Control Receiver, EEBUS", //
+ description = "Controller to optimize energy distribution during peak hours by reducing the inverter output to 0, 30 or 60 percent.")
+@interface Config {
+
+ @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
+ String id() default "limiter0";
+
+ @AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
+ String alias() default "";
+
+ @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?")
+ boolean enabled() default true;
+
+ @AttributeDefinition(name = "Eebus-ID", description = "ID of the EEBUS Bridge.")
+ String eebus_id() default "eebus0";
+
+ String webconsole_configurationFactory_nameHint() default "Controller Ess Ripple Control Receiver [{id}], EEEBUS";
+
+}
\ No newline at end of file
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/EebusPowerProductionLimiter.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/EebusPowerProductionLimiter.java
new file mode 100644
index 00000000000..fdc53c1b75b
--- /dev/null
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/EebusPowerProductionLimiter.java
@@ -0,0 +1,65 @@
+package io.openems.edge.controller.ess.ripplecontrolreceiver.eebus;
+
+import io.openems.common.channel.Level;
+import io.openems.common.types.OpenemsType;
+import io.openems.common.types.OptionsEnum;
+import io.openems.edge.common.channel.Doc;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiterComponent;
+
+import static io.openems.common.channel.PersistencePriority.HIGH;
+
+public interface EebusPowerProductionLimiter extends PowerProductionLimiterComponent {
+
+ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
+ RESTRICTION_REASON(Doc.of(RestrictionModeReason.values())//),
+ .persistencePriority(HIGH)), //
+
+ FAILSAFE_LIMIT(Doc.of(OpenemsType.INTEGER)//
+ .persistencePriority(HIGH)), //
+
+ UPDATE_FAILURE(Doc.of(Level.FAULT)), //
+ ;
+
+ private final Doc doc;
+
+ ChannelId(Doc doc) {
+ this.doc = doc;
+ }
+
+ @Override
+ public Doc doc() {
+ return this.doc;
+ }
+ }
+
+ public enum RestrictionModeReason implements OptionsEnum {
+ NO_LIMIT(-1, "NoLimit"), //
+ ACTIVE_FAILSAFE(1, "ActiveFailsafe"), //
+ LIMITED(2, "Limited"), //
+ ;
+
+ private final int value;
+ private final String name;
+
+ private RestrictionModeReason(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 NO_LIMIT;
+ }
+ }
+
+}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/EebusPowerProductionLimiterImpl.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/EebusPowerProductionLimiterImpl.java
new file mode 100644
index 00000000000..869525de8b3
--- /dev/null
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/eebus/EebusPowerProductionLimiterImpl.java
@@ -0,0 +1,162 @@
+package io.openems.edge.controller.ess.ripplecontrolreceiver.eebus;
+
+import static io.openems.edge.common.channel.ChannelUtils.setValue;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+import org.osgi.service.metatype.annotations.Designate;
+
+import io.openems.common.exceptions.OpenemsError;
+import io.openems.common.referencetarget.GenerateTargetsFromReferences;
+import io.openems.edge.bridge.eebus.api.BridgeEebus;
+import io.openems.edge.bridge.eebus.usecase.powerlimitation.api.ILimitPowerProductionHandler;
+import io.openems.edge.bridge.eebus.usecase.powerlimitation.api.LimitPowerState;
+import io.openems.edge.common.component.AbstractOpenemsComponent;
+import io.openems.edge.common.component.OpenemsComponent;
+import io.openems.edge.common.event.EdgeEventConstants;
+import io.openems.edge.common.sum.GridMode;
+import io.openems.edge.common.sum.Sum;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiter;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiterComponent;
+import io.openems.edge.timedata.api.Timedata;
+import io.openems.edge.timedata.api.TimedataProvider;
+import io.openems.edge.timedata.api.utils.CalculateActiveTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Designate(ocd = Config.class, factory = true)
+@Component(//
+ name = "Controller.Ess.RippleControlReceiver.Eebus", //
+ immediate = true, //
+ configurationPolicy = ConfigurationPolicy.REQUIRE //
+)
+@GenerateTargetsFromReferences({ "eebusBridge" })
+public class EebusPowerProductionLimiterImpl extends AbstractOpenemsComponent
+ implements EebusPowerProductionLimiter, PowerProductionLimiterComponent, PowerProductionLimiter,
+ OpenemsComponent, TimedataProvider, EventHandler, ILimitPowerProductionHandler {
+
+ @Reference
+ private Sum sum;
+
+ @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL)
+ private volatile Timedata timedata = null;
+
+ @Reference(//
+ target = "(&(id=${config.eebus_id})(enabled=true))", policy = ReferencePolicy.STATIC, //
+ policyOption = ReferencePolicyOption.GREEDY, //
+ cardinality = ReferenceCardinality.MANDATORY //
+ )
+ private volatile BridgeEebus eebusBridge;
+
+ @Reference
+ private ConfigurationAdmin cm;
+
+ private Integer maxNominalProductionPower = 15_000;
+ private Integer eebusLimit;
+
+ private final CalculateActiveTime cumulatedRestrictionTime = new CalculateActiveTime(this,
+ PowerProductionLimiterComponent.ChannelId.CUMULATED_RESTRICTION_TIME);
+
+ private final Logger log = LoggerFactory.getLogger(EebusPowerProductionLimiterImpl.class);
+
+ public EebusPowerProductionLimiterImpl() {
+ super(//
+ OpenemsComponent.ChannelId.values(), //
+ PowerProductionLimiterComponent.ChannelId.values(), //
+ EebusPowerProductionLimiter.ChannelId.values() //
+ );
+ }
+
+ @Activate
+ private void activate(ComponentContext context, Config config) {
+ super.activate(context, config.id(), config.alias(), config.enabled());
+ this.eebusBridge.getUseCaseManager().addLimitPowerProductionHandler(this);
+ }
+
+ @Modified
+ protected void modified(ComponentContext context, Config config) {
+ super.modified(context, config.id(), config.alias(), config.enabled()); //
+ }
+
+ @Override
+ @Deactivate
+ protected void deactivate() {
+ super.deactivate();
+ this.eebusBridge.getUseCaseManager().removeLimitPowerProductionHandler(this);
+ }
+
+ @Override
+ public Timedata getTimedata() {
+ return this.timedata;
+ }
+
+ @Override
+ public void setMaxNominalProductionPower(int maxNominalProductionPowerInW) {
+ this.maxNominalProductionPower = maxNominalProductionPowerInW;
+ }
+
+ @Override
+ public long getNominalMaxProduction() {
+ return this.maxNominalProductionPower;
+ }
+
+ @Override
+ public void handleLimitPowerProduction(LimitPowerState state, Double currentLimitInW) {
+ this.logInfo(this.log, "Received eebus limit: " + currentLimitInW);
+ this.eebusLimit = currentLimitInW != null ? currentLimitInW.intValue() : null;
+ }
+
+ protected void run() {
+ try {
+ if (this.sum.getGridMode() != GridMode.ON_GRID) {
+ setValue(this, PowerProductionLimiterComponent.ChannelId.RESTRICTION, null);
+ this.cumulatedRestrictionTime.update(false);
+ return;
+ }
+
+ setValue(this, PowerProductionLimiterComponent.ChannelId.RESTRICTION, this.eebusLimit);
+ this.cumulatedRestrictionTime.update(this.eebusLimit != null);
+ } catch (Exception ex) {
+ // TODO
+ setValue(this, EebusPowerProductionLimiter.ChannelId.UPDATE_FAILURE, true);
+ }
+ }
+
+ @Override
+ public void handleEvent(Event event) {
+ if (!this.isEnabled()) {
+ return;
+ }
+
+ switch (event.getTopic()) {
+ case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE:
+ this.run();
+ break;
+ }
+ }
+
+ @Override
+ public String debugLog() {
+ return "MaxPowerConsumption: " + this.formatInteger(this.maxNominalProductionPower) //
+ + "|Limit: " + this.formatInteger(this.eebusLimit);
+ }
+
+ private String formatInteger(Integer value) {
+ if (value == null) {
+ return "None";
+ } else {
+ return value.toString();
+ }
+ }
+}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/Config.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/Config.java
similarity index 88%
rename from io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/Config.java
rename to io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/Config.java
index b91374d3fbe..0b2da55ee87 100644
--- a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/Config.java
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/Config.java
@@ -1,15 +1,15 @@
-package io.openems.edge.controller.ess.ripplecontrolreceiver;
+package io.openems.edge.controller.ess.ripplecontrolreceiver.powerproduction;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@ObjectClassDefinition(//
- name = "Controller Ess Ripple Control Receiver", //
+ name = "Controller Ess Ripple Control Receiver, FNN-2bit", //
description = "Controller to optimize energy distribution during peak hours by reducing the inverter output to 0, 30 or 60 percent.")
@interface Config {
@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
- String id() default "ctrlEssRippleControlReceiver0";
+ String id() default "limiter0";
@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
String alias() default "";
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/RelaisSignalPowerProductionLimiter.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/RelaisSignalPowerProductionLimiter.java
new file mode 100644
index 00000000000..d34d268dec7
--- /dev/null
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/RelaisSignalPowerProductionLimiter.java
@@ -0,0 +1,41 @@
+package io.openems.edge.controller.ess.ripplecontrolreceiver.powerproduction;
+
+import io.openems.common.channel.Level;
+import io.openems.edge.common.channel.Doc;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.EssRestrictionLevel;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiterComponent;
+
+import static io.openems.common.channel.PersistencePriority.HIGH;
+
+public interface RelaisSignalPowerProductionLimiter extends PowerProductionLimiterComponent {
+ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
+ RESTRICTION_MODE(Doc.of(EssRestrictionLevel.values())//
+ .persistencePriority(HIGH)), //
+
+ UPDATE_FAILURE(Doc.of(Level.WARNING));
+
+ private final Doc doc;
+
+ ChannelId(Doc doc) {
+ this.doc = doc;
+ }
+
+ @Override
+ public Doc doc() {
+ return this.doc;
+ }
+ }
+
+ /**
+ * Represents the current {@link EssRestrictionLevel}.
+ *
+ *
+ * This is determined by the external ripple control signals.
+ *
+ *
+ * @return the current restriction level.
+ */
+ public default EssRestrictionLevel getRestrictionLevel() {
+ return this.channel(ChannelId.RESTRICTION_MODE).value().asEnum();
+ }
+}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/ControllerEssRippleControlReceiverImpl.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/RelaisSignalPowerProductionLimiterImpl.java
similarity index 51%
rename from io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/ControllerEssRippleControlReceiverImpl.java
rename to io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/RelaisSignalPowerProductionLimiterImpl.java
index 76a47c70bb7..717ff92ab6e 100644
--- a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/ControllerEssRippleControlReceiverImpl.java
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/powerproduction/RelaisSignalPowerProductionLimiterImpl.java
@@ -1,9 +1,22 @@
-package io.openems.edge.controller.ess.ripplecontrolreceiver;
-
-import static io.openems.edge.controller.ess.ripplecontrolreceiver.EssRestrictionLevel.NO_RESTRICTION;
-
-import java.util.OptionalInt;
+package io.openems.edge.controller.ess.ripplecontrolreceiver.powerproduction;
+import io.openems.common.exceptions.OpenemsError;
+import io.openems.common.types.ChannelAddress;
+import io.openems.edge.common.channel.BooleanReadChannel;
+import io.openems.edge.common.channel.value.Value;
+import io.openems.edge.common.component.AbstractOpenemsComponent;
+import io.openems.edge.common.component.ComponentManager;
+import io.openems.edge.common.component.OpenemsComponent;
+import io.openems.edge.common.event.EdgeEventConstants;
+import io.openems.edge.common.meta.GridFeedInLimitationType;
+import io.openems.edge.common.sum.GridMode;
+import io.openems.edge.common.sum.Sum;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.EssRestrictionLevel;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiter;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiterComponent;
+import io.openems.edge.timedata.api.Timedata;
+import io.openems.edge.timedata.api.TimedataProvider;
+import io.openems.edge.timedata.api.utils.CalculateActiveTime;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
@@ -15,22 +28,15 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+import org.osgi.service.event.propertytypes.EventTopics;
import org.osgi.service.metatype.annotations.Designate;
-import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
-import io.openems.common.types.ChannelAddress;
-import io.openems.edge.common.channel.BooleanReadChannel;
-import io.openems.edge.common.channel.value.Value;
-import io.openems.edge.common.component.AbstractOpenemsComponent;
-import io.openems.edge.common.component.ComponentManager;
-import io.openems.edge.common.component.OpenemsComponent;
-import io.openems.edge.common.meta.GridFeedInLimitationType;
-import io.openems.edge.common.sum.GridMode;
-import io.openems.edge.common.sum.Sum;
-import io.openems.edge.controller.api.Controller;
-import io.openems.edge.timedata.api.Timedata;
-import io.openems.edge.timedata.api.TimedataProvider;
-import io.openems.edge.timedata.api.utils.CalculateActiveTime;
+import java.util.OptionalInt;
+
+import static io.openems.edge.common.channel.ChannelUtils.setValue;
+import static io.openems.edge.common.event.EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE;
@Designate(ocd = Config.class, factory = true)
@Component(//
@@ -38,8 +44,11 @@
immediate = true, //
configurationPolicy = ConfigurationPolicy.REQUIRE //
)
-public class ControllerEssRippleControlReceiverImpl extends AbstractOpenemsComponent implements //
- ControllerEssRippleControlReceiver, Controller, OpenemsComponent, TimedataProvider {
+@EventTopics({ //
+ TOPIC_CYCLE_BEFORE_PROCESS_IMAGE //
+})
+public class RelaisSignalPowerProductionLimiterImpl extends AbstractOpenemsComponent implements RelaisSignalPowerProductionLimiter,
+ PowerProductionLimiterComponent, PowerProductionLimiter, OpenemsComponent, TimedataProvider, EventHandler {
@Reference
private Sum sum;
@@ -47,36 +56,34 @@ public class ControllerEssRippleControlReceiverImpl extends AbstractOpenemsCompo
@Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL)
private volatile Timedata timedata = null;
- @Reference
- private ConfigurationAdmin cm;
-
@Reference
private ComponentManager componentManager;
- private EssRestrictionLevel currentRestriction = NO_RESTRICTION;
private ChannelAddress zeroPercentChannelAddress;
private ChannelAddress thirtyPercentChannelAddress;
private ChannelAddress sixtyPercentChannelAddress;
+ private Integer maxNominalProductionPowerInW;
+
private final CalculateActiveTime cumulatedRestrictionTime = new CalculateActiveTime(this,
- ControllerEssRippleControlReceiver.ChannelId.CUMULATED_RESTRICTION_TIME);
+ PowerProductionLimiterComponent.ChannelId.CUMULATED_RESTRICTION_TIME);
- public ControllerEssRippleControlReceiverImpl() {
+ public RelaisSignalPowerProductionLimiterImpl() {
super(//
OpenemsComponent.ChannelId.values(), //
- Controller.ChannelId.values(), //
- ControllerEssRippleControlReceiver.ChannelId.values() //
+ PowerProductionLimiterComponent.ChannelId.values(), //
+ RelaisSignalPowerProductionLimiter.ChannelId.values() //
);
}
@Activate
- private void activate(ComponentContext context, Config config) throws OpenemsNamedException {
+ private void activate(ComponentContext context, Config config) throws OpenemsError.OpenemsNamedException {
super.activate(context, config.id(), config.alias(), config.enabled());
this.applyConfig(config);
}
@Modified
- protected void modified(ComponentContext context, Config config) throws OpenemsNamedException {
+ protected void modified(ComponentContext context, Config config) throws OpenemsError.OpenemsNamedException {
super.modified(context, config.id(), config.alias(), config.enabled()); //
this.applyConfig(config);
}
@@ -87,39 +94,54 @@ protected void deactivate() {
super.deactivate();
}
- @Override
- public void run() throws OpenemsNamedException {
- var currentRestriction = NO_RESTRICTION;
- if (this.sum.getGridMode().equals(GridMode.ON_GRID)) {
- currentRestriction = EssRestrictionLevel.getRestrictionLevelByPriority(//
- this.isRestrictionActive(this.zeroPercentChannelAddress), //
- this.isRestrictionActive(this.thirtyPercentChannelAddress), //
- this.isRestrictionActive(this.sixtyPercentChannelAddress));
- }
+ private void applyConfig(Config config) throws OpenemsError.OpenemsNamedException {
+ this.zeroPercentChannelAddress = ChannelAddress.fromString(config.inputChannelAddress1());
+ this.thirtyPercentChannelAddress = ChannelAddress.fromString(config.inputChannelAddress2());
+ this.sixtyPercentChannelAddress = ChannelAddress.fromString(config.inputChannelAddress3());
+ }
- switch (currentRestriction) {
- case NO_RESTRICTION -> {
- this.cumulatedRestrictionTime.update(false);
- }
- case ZERO_PERCENT, THIRTY_PERCENT, SIXTY_PERCENT -> {
- // This may split into different cumulated times for each restriction level
- this.cumulatedRestrictionTime.update(true);
- }
+ protected void run() {
+ try {
+ if (this.sum.getGridMode() != GridMode.ON_GRID) {
+ this.setRestriction(EssRestrictionLevel.NO_RESTRICTION);
+ return;
+ }
+
+ this.setRestriction(this.calculateRestrictionByRelayPorts());
+ setValue(this, RelaisSignalPowerProductionLimiter.ChannelId.UPDATE_FAILURE, false);
+ } catch (Exception ex) {
+ // TODO
+ setValue(this, RelaisSignalPowerProductionLimiter.ChannelId.UPDATE_FAILURE, true);
}
+ }
- this.currentRestriction = currentRestriction;
- this._setRestrictionMode(currentRestriction);
+ protected EssRestrictionLevel calculateRestrictionByRelayPorts() throws OpenemsError.OpenemsNamedException {
+ return EssRestrictionLevel.getRestrictionLevelByPriority(//
+ this.isRelayPortActive(this.zeroPercentChannelAddress), //
+ this.isRelayPortActive(this.thirtyPercentChannelAddress), //
+ this.isRelayPortActive(this.sixtyPercentChannelAddress));
}
- protected boolean isRestrictionActive(ChannelAddress inputChannelAddress) throws OpenemsNamedException {
+ private boolean isRelayPortActive(ChannelAddress inputChannelAddress) throws OpenemsError.OpenemsNamedException {
BooleanReadChannel inputChannel = this.componentManager.getChannel(inputChannelAddress);
// 0/1 is reversed on relays board
return !inputChannel.value().orElse(true);
}
+ protected void setRestriction(EssRestrictionLevel restrictionLevel) {
+ setValue(this, RelaisSignalPowerProductionLimiter.ChannelId.RESTRICTION_MODE, restrictionLevel);
+ if (restrictionLevel == EssRestrictionLevel.NO_RESTRICTION) {
+ this.cumulatedRestrictionTime.update(false);
+ setValue(this, PowerProductionLimiterComponent.ChannelId.RESTRICTION, null);
+ } else {
+ this.cumulatedRestrictionTime.update(true);
+ setValue(this, PowerProductionLimiterComponent.ChannelId.RESTRICTION, restrictionLevel.getLimitationFactor() * this.maxNominalProductionPowerInW);
+ }
+ }
+
@Override
- public EssRestrictionLevel essRestrictionLevel() {
- return this.currentRestriction;
+ public String debugLog() {
+ return "Current limitation: " + this.getRestrictionLevel().getName();
}
@Override
@@ -127,35 +149,42 @@ public Timedata getTimedata() {
return this.timedata;
}
- private void applyConfig(Config config) throws OpenemsNamedException {
- this.zeroPercentChannelAddress = ChannelAddress.fromString(config.inputChannelAddress1());
- this.thirtyPercentChannelAddress = ChannelAddress.fromString(config.inputChannelAddress2());
- this.sixtyPercentChannelAddress = ChannelAddress.fromString(config.inputChannelAddress3());
+ @Override
+ public void handleEvent(Event event) {
+ if (!this.isEnabled()) {
+ return;
+ }
+
+ switch (event.getTopic()) {
+ case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE:
+ this.run();
+ break;
+ }
}
@Override
- public String debugLog() {
- return "Current limitation: " + this.getRestrictionMode().getName();
+ public void setMaxNominalProductionPower(int maxNominalProductionPowerInW) {
+ this.maxNominalProductionPowerInW = maxNominalProductionPowerInW;
}
/**
* Calculates the feed-in limit from the meta component's grid feed-in
* limitation.
- *
+ *
* @param type the type of grid feed-in limitation
* @param limit the limit value (in W) if type is DYNAMIC_LIMITATION
* @return the dynamic feed-in limit as OptionalInt
*/
public static OptionalInt feedInLimitFromMetaLimits(GridFeedInLimitationType type, Value limit) {
return switch (type) {
- case DYNAMIC_LIMITATION -> {
- if (limit.isDefined()) {
- yield OptionalInt.of(limit.get());
+ case DYNAMIC_LIMITATION -> {
+ if (limit.isDefined()) {
+ yield OptionalInt.of(limit.get());
+ }
+ yield OptionalInt.empty();
}
- yield OptionalInt.empty();
- }
- case NO_LIMITATION -> OptionalInt.empty();
- case UNDEFINED -> OptionalInt.empty();
+ case NO_LIMITATION -> OptionalInt.empty();
+ case UNDEFINED -> OptionalInt.empty();
};
}
-}
\ No newline at end of file
+}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/test/DummyPowerProductionLimiter.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/test/DummyPowerProductionLimiter.java
new file mode 100644
index 00000000000..8a79091627b
--- /dev/null
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/test/DummyPowerProductionLimiter.java
@@ -0,0 +1,55 @@
+package io.openems.edge.controller.ess.ripplecontrolreceiver.test;
+
+import io.openems.edge.common.component.OpenemsComponent;
+import io.openems.edge.common.test.AbstractDummyOpenemsComponent;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.EssRestrictionLevel;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiterComponent;
+
+import java.util.function.Supplier;
+
+public class DummyPowerProductionLimiter extends AbstractDummyOpenemsComponent
+ implements PowerProductionLimiterComponent {
+
+ private int maxNominalProductionPowerInW;
+ private Supplier gridFeedInLimitFunc;
+
+ public DummyPowerProductionLimiter(String id) {
+ super(id, //
+ OpenemsComponent.ChannelId.values(), //
+ PowerProductionLimiterComponent.ChannelId.values() //
+ );
+ }
+
+ @Override
+ protected DummyPowerProductionLimiter self() {
+ return this;
+ }
+
+ @Override
+ public void setMaxNominalProductionPower(int maxNominalProductionPowerInW) {
+ this.maxNominalProductionPowerInW = maxNominalProductionPowerInW;
+ }
+
+ public DummyPowerProductionLimiter withGridFeedInLimit(Integer gridFeedInLimit) {
+ if (gridFeedInLimit == null) {
+ this.gridFeedInLimitFunc = null;
+ } else {
+ this.gridFeedInLimitFunc = () -> gridFeedInLimit;
+ }
+ return this.self();
+ }
+
+ public DummyPowerProductionLimiter withRestrictionLevel(EssRestrictionLevel restrictionLevel) {
+ if (restrictionLevel == null || restrictionLevel == EssRestrictionLevel.NO_RESTRICTION) {
+ this.gridFeedInLimitFunc = null;
+ } else {
+ this.gridFeedInLimitFunc = () -> (int) (this.maxNominalProductionPowerInW * restrictionLevel.getLimitationFactor());
+ }
+ return this.self();
+ }
+
+ @Override
+ public Integer getGridFeedInLimit() {
+ return this.gridFeedInLimitFunc.get();
+ }
+}
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/test/DummyRippleControlReceiver.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/test/DummyRippleControlReceiver.java
deleted file mode 100644
index dbce9f8ddae..00000000000
--- a/io.openems.edge.controller.ess.ripplecontrolreceiver/src/io/openems/edge/controller/ess/ripplecontrolreceiver/test/DummyRippleControlReceiver.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package io.openems.edge.controller.ess.ripplecontrolreceiver.test;
-
-import io.openems.edge.common.component.OpenemsComponent;
-import io.openems.edge.common.test.AbstractDummyOpenemsComponent;
-import io.openems.edge.controller.api.Controller;
-import io.openems.edge.controller.ess.ripplecontrolreceiver.ControllerEssRippleControlReceiver;
-import io.openems.edge.controller.ess.ripplecontrolreceiver.EssRestrictionLevel;
-
-/**
- * Dummy implementation of {@link ControllerEssRippleControlReceiver} for testing.
- */
-public class DummyRippleControlReceiver extends AbstractDummyOpenemsComponent
- implements ControllerEssRippleControlReceiver {
-
- private EssRestrictionLevel restrictionLevel = EssRestrictionLevel.NO_RESTRICTION;
-
- public DummyRippleControlReceiver(String id) {
- super(id, //
- OpenemsComponent.ChannelId.values(), //
- Controller.ChannelId.values(), //
- ControllerEssRippleControlReceiver.ChannelId.values());
- }
-
- @Override
- protected DummyRippleControlReceiver self() {
- return this;
- }
-
- @Override
- public void run() {
- // Dummy implementation - does nothing
- }
-
- @Override
- public EssRestrictionLevel essRestrictionLevel() {
- return this.restrictionLevel;
- }
-
- /**
- * Sets the restriction level for this dummy instance.
- *
- * @param restrictionLevel the {@link EssRestrictionLevel} to set
- * @return myself for method chaining
- */
- public DummyRippleControlReceiver withRestrictionLevel(EssRestrictionLevel restrictionLevel) {
- this.restrictionLevel = restrictionLevel;
- return this.self();
- }
-}
-
diff --git a/io.openems.edge.controller.ess.ripplecontrolreceiver/test/io/openems/edge/controller/ess/ripplecontrolreceiver/MyConfig.java b/io.openems.edge.controller.ess.ripplecontrolreceiver/test/io/openems/edge/controller/ess/ripplecontrolreceiver/MyConfig.java
index 12fafcb152b..beb9d0a826d 100644
--- a/io.openems.edge.controller.ess.ripplecontrolreceiver/test/io/openems/edge/controller/ess/ripplecontrolreceiver/MyConfig.java
+++ b/io.openems.edge.controller.ess.ripplecontrolreceiver/test/io/openems/edge/controller/ess/ripplecontrolreceiver/MyConfig.java
@@ -1,6 +1,7 @@
package io.openems.edge.controller.ess.ripplecontrolreceiver;
import io.openems.common.test.AbstractComponentConfig;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.powerproduction.Config;
@SuppressWarnings("all")
public class MyConfig extends AbstractComponentConfig implements Config {
diff --git a/io.openems.edge.core/bnd.bnd b/io.openems.edge.core/bnd.bnd
index 75bec006783..9f9e1af8b1c 100644
--- a/io.openems.edge.core/bnd.bnd
+++ b/io.openems.edge.core/bnd.bnd
@@ -8,6 +8,7 @@ Bundle-Version: 1.0.0.${tstamp}
com.fazecast.jSerialComm,\
com.squareup.okhttp3,\
io.openems.common,\
+ io.openems.common.bridge.eebus,\
io.openems.common.bridge.http,\
io.openems.edge.common,\
io.openems.edge.controller.api,\
diff --git a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java
index 184916e0660..5138d383582 100644
--- a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java
+++ b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java
@@ -197,7 +197,7 @@ private Prediction getPredictionSum(Sum.ChannelId channelId) {
PRODUCTION_TO_CONSUMPTION_POWER, PRODUCTION_TO_CONSUMPTION_ENERGY, PRODUCTION_TO_ESS_POWER,
PRODUCTION_TO_ESS_ENERGY, PRODUCTION_TO_GRID_POWER, PRODUCTION_TO_GRID_ENERGY,
- HAS_IGNORED_COMPONENT_STATES ->
+ HAS_IGNORED_COMPONENT_STATES, BATTERY_INVERTER_MAX_APPARENT_POWER ->
EMPTY_PREDICTION;
case UNMANAGED_CONSUMPTION_ACTIVE_POWER ->
diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java
index da3f8b6dbb5..d5ab2897bb4 100644
--- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java
+++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java
@@ -20,6 +20,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
+import io.openems.edge.controller.ess.ripplecontrolreceiver.PowerProductionLimiter;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
@@ -75,7 +76,6 @@
import io.openems.edge.common.taskmanager.Priority;
import io.openems.edge.common.type.TypeUtils;
import io.openems.edge.common.update.Updateable;
-import io.openems.edge.controller.ess.ripplecontrolreceiver.ControllerEssRippleControlReceiver;
import io.openems.edge.ess.power.api.Power;
import io.openems.edge.goodwe.batteryinverter.statemachine.Context;
import io.openems.edge.goodwe.batteryinverter.statemachine.StateMachine;
@@ -153,7 +153,7 @@ public class GoodWeBatteryInverterImpl extends AbstractGoodWe implements GoodWeB
private Meta meta;
@Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL)
- protected volatile ControllerEssRippleControlReceiver rcr;
+ protected volatile PowerProductionLimiter powerProductionLimiter;
@Override
@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
@@ -785,9 +785,11 @@ private void handleGridFeed(Config config, GridFeedInLimitationType limitType) t
}
// Limit from Ripple Control Receiver (Minimum of both limits)
- if (this.rcr != null && this.rcr.isEnabled()) {
+ if (this.powerProductionLimiter != null) {
+ this.powerProductionLimiter.setMaxNominalProductionPower(maxApparentPower);
+
enableFeedInLimit = true;
- gridFeedInLimit = Math.min(gridFeedInLimit, this.rcr.getDynamicGridFeedInLimit(maxApparentPower));
+ gridFeedInLimit = Math.min(gridFeedInLimit, this.powerProductionLimiter.getGridFeedInLimit());
}
this.handleFeedInSetting(enableFeedInLimit, gridFeedInLimit, this.getGoodweType());