Skip to content

Commit

Permalink
Further development and rework of the EVCS (Electric Vehicle Charging…
Browse files Browse the repository at this point in the history
… Station) components (#1263)

* Changed small time issues in the keba implementation.
Added unknown state in the UI.

* Added EvcsPower and therefore a new Filter "RampFilter".
Because of that, every ManagedEvcs impl. needs a the current reference of the EvcsPowerComponent, to have the global ramp filter and in the future other things of the EvcsPower Interface.
Initial rework of the EvcsCluster.
Small format changes.

* Wrote new test for the EVCS controller.

* changed a testcase in the EvcsController test, to check the changes in the config instead of checking the _Property Channels.

* Evcs Cluster: Created a new unit test and changed a few things in the Evcs Cluster components.

* EvcsController: Changed code for the disabled charging mode and adapted the tests

* EvcsCluster: Changed code for the minimumGuaranteed power that a charging station needs and adapted the tests

* Changed the hadling of the maximum Power of an EVCS. It will be used that value of the maximumum for calculation, but the sent charge power will be like before.

* General changes for the EnergySession calculation if there's only a total energy value.
Adjustments to the ABL implementation

* Send also a chargePowerRequest of 0 if charging is disabled

* Added logic, to reduce the value calculated from the "maximum allowed discharge power" of the ess, to avoid certain jumps to zero, if the essSoc gets lower.

* Added a total energy value in the EVCS Nature (ACTIVE_CONSUMPTION_ENERGY).
Set the total energy and session energy depending on one energy value form the chargingstation (depending on the chargingstation we get the total or the session energy).

* Rework of the energy calculation.

Co-authored-by: Stefan Feilmeier <[email protected]>
  • Loading branch information
sebastianasen and sfeilmeier authored Nov 19, 2020
1 parent b564862 commit ab3cc8e
Show file tree
Hide file tree
Showing 50 changed files with 2,517 additions and 860 deletions.
2 changes: 2 additions & 0 deletions io.openems.edge.application/EdgeApp.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
bnd.identity;id='io.openems.edge.ess.sma',\
bnd.identity;id='io.openems.edge.ess.streetscooter',\
bnd.identity;id='io.openems.edge.evcs.cluster',\
bnd.identity;id='io.openems.edge.evcs.core',\
bnd.identity;id='io.openems.edge.evcs.keba.kecontact',\
bnd.identity;id='io.openems.edge.evcs.ocpp.abl',\
bnd.identity;id='io.openems.edge.evcs.ocpp.common',\
Expand Down Expand Up @@ -223,6 +224,7 @@
io.openems.edge.ess.streetscooter;version=snapshot,\
io.openems.edge.evcs.api;version=snapshot,\
io.openems.edge.evcs.cluster;version=snapshot,\
io.openems.edge.evcs.core;version=snapshot,\
io.openems.edge.evcs.keba.kecontact;version=snapshot,\
io.openems.edge.evcs.ocpp.abl;version=snapshot,\
io.openems.edge.evcs.ocpp.common;version=snapshot,\
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package io.openems.edge.ess.core.power;

import io.openems.edge.common.filter.PidFilter;
package io.openems.edge.common.filter;

/**
* This implementation ignores the PID filter and instead just returns the
* unfiltered target value. It is used when {@link PowerComponent} is configured
* to disable PID filter.
* unfiltered target value - making sure it is within the allowed minimum and
* maximum limits. It is used when {@link PowerComponent} is configured to
* disable PID filter.
*/
public class DisabledPidFilter extends PidFilter {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.openems.edge.common.filter;

/**
* This implementation ignores the Ramp filter and instead just returns the
* unfiltered target value - making sure it is within the allowed minimum and
* maximum limits. It is used when the using Component is configured to disable
* Ramp filter.
*/
public class DisabledRampFilter extends RampFilter {

@Override
public int applyRampFilter(int input, int target) {
return this.applyLowHighLimits(target);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package io.openems.edge.common.filter;

import io.openems.common.exceptions.OpenemsException;

/**
* A controller that increases the input by a given increase rate.
*/
public class RampFilter {

public static final double DEFAULT_INCREASE_RATE = 0.05;

private double increasingRate;

private Integer lowLimit = null;
private Integer highLimit = null;

/**
* Creates a RampFilter
*
* @param increasingRate the rate of increase
*/
public RampFilter(double increasingRate) {
this.increasingRate = DEFAULT_INCREASE_RATE;
if (increasingRate > 0 && increasingRate < 1) {
this.increasingRate = increasingRate;
}
}

/**
* Creates a RampFilter using default values
*/
public RampFilter() {
this(DEFAULT_INCREASE_RATE);
}

/**
* Limit the output value.
*
* @param lowLimit lowest allowed output value
* @param highLimit highest allowed output value
*/
public void setLimits(Integer lowLimit, Integer highLimit) {
if (lowLimit != null && highLimit != null && lowLimit > highLimit) {
throw new IllegalArgumentException(
"Given LowLimit [" + lowLimit + "] is higher than HighLimit [" + highLimit + "]");
}
this.lowLimit = lowLimit;
this.highLimit = highLimit;
}

/**
* Apply the filter using the current Channel value as input and the target
* value.
*
* @param input the input value, e.g. the measured Channel value
* @param target the target value
* @return the filtered set-point value
* @throws OpenemsException
*/
public int applyRampFilter(int input, int target) throws OpenemsException {

// Pre-process the target value: apply output value limits
target = this.applyLowHighLimits(target);

// Make sure that there is a highLimit set
if (this.highLimit == null) {
throw new OpenemsException(
"No high limit given in EvcsFilter. Please call setLimits bevore applying the filter.");
}

// We are already there
if (input == target) {
return target;
}

// Calculate the next additional power
double additionalPower = this.highLimit * this.increasingRate;

// Next power
double output = input;
if (input < target) {
// input should increase
output += additionalPower;
output = output > target ? target : output;
} else {
// input should decrease
output -= additionalPower;
output = output < target ? target : output;
}

// Post-process the output value: convert to integer
return Math.round((float) output);
}

/**
* Applies the configured PID low and high limits to a value.
*
* @param value the input value
* @return the value within low and high limit
*/
protected int applyLowHighLimits(int value) {
if (this.lowLimit != null && value < this.lowLimit) {
value = this.lowLimit;
}
if (this.highLimit != null && value > this.highLimit) {
value = this.highLimit;
}
return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ public class ChargingLowerThanTargetHandler {
*/
private static final int MAXIMUM_OUT_OF_RANGE_TRIES = 3;
private int outOfRangeCounter = 0;
private static final double CHARGING_TARGET_MAX_DIFFERENCE_PERCENT = 0.10; // 10%
private static final int CHECK_CHARGING_TARGET_DIFFERENCE_TIME = 30; // sec
private static final double CHARGING_TARGET_MAX_DIFFERENCE_PERCENT = 0.15; // 10%
private static final int CHECK_CHARGING_TARGET_DIFFERENCE_TIME = 45; // sec
private LocalDateTime lastChargingCheck = LocalDateTime.now();
private Integer maximumChargePower = null; // W

private final EvcsControllerImpl parent;
private final EvcsController parent;

public ChargingLowerThanTargetHandler(EvcsControllerImpl parent) {
public ChargingLowerThanTargetHandler(EvcsController parent) {
this.parent = parent;
}

Expand Down Expand Up @@ -53,20 +54,46 @@ protected boolean isLower(ManagedEvcs evcs) throws InvalidValueException {

/**
* Check if the difference between the requested charging target and the real
* charging power is higher than the CHARGING_TARGET_MAX_DIFFERENCE.
* charging power is higher than the CHARGING_TARGET_MAX_DIFFERENCE. If it
* returns true, it is setting the maximumPower.
*
* @param evcs EVCS
* @return true if the difference is too high
* @throws InvalidValueException invalidValueException
*/
protected boolean isChargingLowerThanTarget(ManagedEvcs evcs) throws InvalidValueException {
int chargingPower = evcs.getChargePower().orElse(0);
int chargingPowerTarget = evcs.getSetChargePowerLimit().orElse(evcs.getMaximumHardwarePower().getOrError());
if (chargingPowerTarget - chargingPower > chargingPowerTarget * CHARGING_TARGET_MAX_DIFFERENCE_PERCENT) {
int chargePower = evcs.getChargePower().orElse(0);
int chargePowerTarget = evcs.getSetChargePowerLimit().orElse(evcs.getMaximumHardwarePower().getOrError());

if (chargePowerTarget - chargePower > chargePowerTarget * CHARGING_TARGET_MAX_DIFFERENCE_PERCENT) {
this.maximumChargePower = calculateMaximumPower(chargePower);
this.lastChargingCheck = LocalDateTime.now();
return true;
}
this.maximumChargePower = null;
return false;
}

/**
* Returns the calculated maximum charge power depending on the current charge
* power and the current maximum charge power.
*
* @param chargingPower current charge power
* @return the current charge power or one of the past charge power values
*/
private Integer calculateMaximumPower(int currentChargePower) {
if (this.maximumChargePower == null) {
return currentChargePower;
}
return currentChargePower > this.maximumChargePower ? currentChargePower : this.maximumChargePower;
}

/**
* Maximum charge power of the EV depending on the last {@link #isChargingLowerThanTarget(ManagedEvcs)} try's.
*
* @return The maximum charge power of the EV.
*/
protected Integer getMaximumChargePower() {
return this.maximumChargePower;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@

@AttributeDefinition(name = "Energy limit in this session in [Wh]", description = "Set the Energylimit in this Session in Wh. The charging station will only charge till this limit; '0' is no limit.")
int energySessionLimit() default 0;

@AttributeDefinition(name = "Evcs target filter", description = "This is auto-generated by 'Evcs-ID'.")
String evcs_target() default "";

String webconsole_configurationFactory_nameHint() default "Controller Electric Vehicle Charging Station [{id}]";

Expand Down
Loading

0 comments on commit ab3cc8e

Please sign in to comment.