diff --git a/edge/pom.xml b/edge/pom.xml index 2857cf32477..1e01a4260bd 100644 --- a/edge/pom.xml +++ b/edge/pom.xml @@ -5,8 +5,8 @@ http://openems.io io.openems edge - - 2018.6.0-SNAPSHOT + 2018.6.0 + jar https://github.com/OpenEMS/openems diff --git a/edge/src/io/openems/App.java b/edge/src/io/openems/App.java index 00b7337ef0e..cc0d2fe140e 100644 --- a/edge/src/io/openems/App.java +++ b/edge/src/io/openems/App.java @@ -38,8 +38,8 @@ public class App { private final static Logger log = LoggerFactory.getLogger(App.class); - // public final static String OPENEMS_VERSION = "2018.5.0"; - public final static String OPENEMS_VERSION = "2018.6.0-SNAPSHOT"; + public final static String OPENEMS_VERSION = "2018.6.0"; + // public final static String OPENEMS_VERSION = "2018.7.0-SNAPSHOT"; public static void main(String[] args) { log.info("OpenEMS version [" + OPENEMS_VERSION + "] started"); diff --git a/edge/src/io/openems/core/utilities/AsymmetricPower.java b/edge/src/io/openems/core/utilities/AsymmetricPower.java index c52f356b82a..ec8fc0de514 100644 --- a/edge/src/io/openems/core/utilities/AsymmetricPower.java +++ b/edge/src/io/openems/core/utilities/AsymmetricPower.java @@ -318,7 +318,7 @@ public void reducePower(ReductionType reductionType) throws WriteChannelExceptio break; } } catch (InvalidValueException e) { - log.error("Failed to reduce power", e); + log.error("Failed to reduce power: " + e.getMessage()); } // log.info( // "Reduce activePower L1:[{}]->[{}], L2:[{}]->[{}],L3:[{}]->[{}] " diff --git a/edge/src/io/openems/impl/controller/channelthreshold/ChannelThresholdController.java b/edge/src/io/openems/impl/controller/channelthreshold/ChannelThresholdController.java index c561fdcc65e..35c5c273c39 100644 --- a/edge/src/io/openems/impl/controller/channelthreshold/ChannelThresholdController.java +++ b/edge/src/io/openems/impl/controller/channelthreshold/ChannelThresholdController.java @@ -40,16 +40,16 @@ /* * Example config: *
- * {
- *   "class": "io.openems.impl.controller.channelthreshold.ChannelThresholdController",
- *   "priority": 65,
- *   "thresholdChannelAddress": "ess0/Soc",
- *   "outputChannelAddress": "output0/1",
- *   "lowerThreshold": 75,
- *   "upperThreshold": 80,
- *   "invertOutput": true,
- *   "hysteresis": 2
- * }
+{
+	"class": "io.openems.impl.controller.channelthreshold.ChannelThresholdController",
+	"priority": 65,
+	"thresholdChannelAddress": "ess0/Soc",
+	"outputChannelAddress": "output0/1",
+	"lowerThreshold": 75,
+	"upperThreshold": 80,
+	"invertOutput": true,
+	"hysteresis": 2
+}
  * 
*/ @@ -77,7 +77,6 @@ public ChannelThresholdController(String thingId) { private ThingRepository repo = ThingRepository.getInstance(); private ReadChannel thresholdChannel; private WriteChannel outputChannel; - private boolean isActive = false; /* * Config @@ -128,59 +127,206 @@ public ChannelThresholdController(String thingId) { @ChannelInfo(title = "Invert-Output", description = "True if the digital output should be inverted.", type = Boolean.class) public ConfigChannel invertOutput = new ConfigChannel("invertOutput", this).defaultValue(false); + private enum State { + UNDEFINED, /* Unknown state on first start */ + BELOW_LOW, /* Value is smaller than the low threshold */ + PASS_LOW_COMING_FROM_BELOW, /* Value just passed the low threshold. Last value was even lower. */ + PASS_LOW_COMING_FROM_ABOVE, /* Value just passed the low threshold. Last value was higher. */ + BETWEEN_LOW_AND_HIGH, /* Value is between low and high threshold */ + PASS_HIGH_COMING_FROM_BELOW, /* Value just passed the high threshold. Last value was lower. */ + PASS_HIGH_COMING_FROM_ABOVE, /* Value just passed the high threshold. Last value was higher. */ + ABOVE_HIGH /* Value is bigger than the high threshold */ + } + + /** + * The current state in the State Machine + */ + private State state = State.UNDEFINED; + + /** + * Should the hysteresis be applied on passing high threshold? + */ + private boolean applyHighHysteresis = true; + /** + * Should the hysteresis be applied on passing low threshold? + */ + private boolean applyLowHysteresis = true; + /* * Methods */ @Override public void run() { - // Check if all parameters are available - long threshold; + /* + * Check if all parameters are available + */ + long value; long lowerThreshold; long upperThreshold; long hysteresis; - boolean invertOutput; try { - threshold = this.thresholdChannel.value(); + value = this.thresholdChannel.value(); lowerThreshold = this.lowerThreshold.value(); upperThreshold = this.upperThreshold.value(); hysteresis = this.hysteresis.value(); - invertOutput = this.invertOutput.value(); } catch (InvalidValueException e) { log.error("ChannelThresholdController error: " + e.getMessage()); return; } - try { - if (isActive) { - if (threshold < lowerThreshold || threshold > upperThreshold + hysteresis) { - isActive = false; - } else { - on(invertOutput); + + /* + * State Machine + */ + switch (this.state) { + case UNDEFINED: + if (value < lowerThreshold) { + this.state = State.BELOW_LOW; + } else if (value > upperThreshold) { + this.state = State.ABOVE_HIGH; + } else { + this.state = State.BETWEEN_LOW_AND_HIGH; + } + break; + + case BELOW_LOW: + /* + * Value is smaller than the low threshold -> always OFF + */ + if (value >= lowerThreshold) { + this.state = State.PASS_LOW_COMING_FROM_BELOW; + break; + } + + this.off(); + break; + + case PASS_LOW_COMING_FROM_BELOW: + /* + * Value just passed the low threshold coming from below -> turn ON + */ + this.on(); + this.applyLowHysteresis = true; + this.state = State.BETWEEN_LOW_AND_HIGH; + break; + + case BETWEEN_LOW_AND_HIGH: + /* + * Value is between low and high threshold -> always ON + */ + // evaluate if hysteresis is necessary + if (value >= lowerThreshold + hysteresis) { + this.applyLowHysteresis = false; // do not apply low hysteresis anymore + } + if (value <= upperThreshold - hysteresis) { + this.applyHighHysteresis = false; // do not apply high hysteresis anymore + } + + /* + * Check LOW threshold + */ + if (applyLowHysteresis) { + if (value <= lowerThreshold - hysteresis) { + // pass low with hysteresis + this.state = State.PASS_LOW_COMING_FROM_ABOVE; + break; } } else { - if (threshold >= lowerThreshold + hysteresis && threshold <= upperThreshold) { - isActive = true; - } else { - off(invertOutput); + if (value <= lowerThreshold) { + // pass low, not applying hysteresis + this.state = State.PASS_LOW_COMING_FROM_ABOVE; + break; } } - } catch (WriteChannelException e) { - log.error("Failed to write Channel[" + outputChannel.address() + "]: " + e.getMessage()); + + /* + * Check HIGH threshold + */ + if (applyHighHysteresis) { + if (value >= upperThreshold + hysteresis) { + // pass high with hysteresis + this.state = State.PASS_HIGH_COMING_FROM_BELOW; + break; + } + } else { + if (value >= upperThreshold) { + // pass high, not applying hysteresis + this.state = State.PASS_HIGH_COMING_FROM_BELOW; + break; + } + } + + // Default: not switching the State -> always ON + this.on(); + break; + + case PASS_HIGH_COMING_FROM_BELOW: + /* + * Value just passed the high threshold coming from below -> turn OFF + */ + this.off(); + this.state = State.ABOVE_HIGH; + break; + + case PASS_LOW_COMING_FROM_ABOVE: + /* + * Value just passed the low threshold from above -> turn OFF + */ + this.off(); + this.state = State.BELOW_LOW; + break; + + case PASS_HIGH_COMING_FROM_ABOVE: + /* + * Value just passed the high threshold coming from above -> turn ON + */ + this.on(); + this.applyHighHysteresis = true; + this.state = State.BETWEEN_LOW_AND_HIGH; + break; + + case ABOVE_HIGH: + /* + * Value is bigger than the high threshold -> always OFF + */ + if (value <= upperThreshold) { + this.state = State.PASS_HIGH_COMING_FROM_ABOVE; + } + + this.off(); + break; } } - private void on(boolean invertOutput) throws WriteChannelException { - Optional currentValueOpt = this.outputChannel.valueOptional(); - if (!currentValueOpt.isPresent() || currentValueOpt.get() != (true ^ invertOutput)) { - log.info("Set output [" + this.outputChannel.address() + "] ON."); - outputChannel.pushWrite(true ^ invertOutput); - } + /** + * Switch the output ON + */ + private void on() { + this.setOutput(true); } - private void off(boolean invertOutput) throws WriteChannelException { + /** + * Switch the output OFF + */ + private void off() { + this.setOutput(false); + } + + /** + * Helper function to switch an output if it was not switched before. + * + * @param value + * true to switch ON, false to switch ON; is inverted if 'invertOutput' config is set + */ + private void setOutput(boolean value) { Optional currentValueOpt = this.outputChannel.valueOptional(); - if (!currentValueOpt.isPresent() || currentValueOpt.get() != (false ^ invertOutput)) { - log.info("Set output [" + this.outputChannel.address() + "] OFF."); - outputChannel.pushWrite(false ^ invertOutput); + boolean invertOutput = this.invertOutput.valueOptional().orElse(false); + if (!currentValueOpt.isPresent() || currentValueOpt.get() != (value ^ invertOutput)) { + log.info("Set output [" + this.outputChannel.address() + "] " + (value ? "ON" : "OFF") + "."); + try { + outputChannel.pushWrite(value ^ invertOutput); + } catch (WriteChannelException e) { + log.error("ChannelThresholdController error: " + e.getMessage()); + } } } diff --git a/ui/package-lock.json b/ui/package-lock.json index 5c380648a53..2af8a1c2af9 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "openems-ui", - "version": "2018.6.0-SNAPSHOT", + "version": "2018.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/ui/package.json b/ui/package.json index 37c2b7dd0e4..24fc7aaa77f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "openems-ui", - "version": "2018.6.0-SNAPSHOT", + "version": "2018.6.0", "license": "AGPL", "scripts": { "ng": "ng", diff --git a/ui/pom.xml b/ui/pom.xml index edbc299a000..0152463dfd8 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -7,8 +7,8 @@ io.openems pom - - 2018.6.0-SNAPSHOT + 2018.6.0 + edge pom diff --git a/ui/src/app/about/about.component.html b/ui/src/app/about/about.component.html index 8b2b03203fa..04bfa916b60 100644 --- a/ui/src/app/about/about.component.html +++ b/ui/src/app/about/about.component.html @@ -11,19 +11,19 @@ About.Fenecon
  • - About.Fems + About.Fems
  • About.Sourcecode
  • -
  • - - About.Build: 2018.6.0-SNAPSHOT + + About.Build: 2018.6 (2018-04-25)
  • +

    @@ -36,4 +36,4 @@ - + \ No newline at end of file