Skip to content

Commit

Permalink
FEMS Backports (#2435)
Browse files Browse the repository at this point in the history
* Sum ESS State-of-Charge: unify calculation

Unify the calculation of a average State-of-Charge of multiple Energy Storage Systems. Before, the calculation was different for EssCluster and Sum. Adding fully tested `CalculateSoC` helper class.

* Update Home 20 & 30 Charger config

* Emergency Capacity Reserve Controller: fix handling of negative AC production (#878)

Before, this could lead to discharge in FORCE_CHARGE mode, when AC production was negative.

* GoodWe EmergencyMeter: Fix scale factor for current and voltage

Fix scale factor for current and voltage.
Old implementation would get the values as Volt or Amps, but should be mV and mA.

* UI: Display message for planned capacity extension

Display message for **planned** capacity extension in storage flat-widget

* AppCenter: automatically add UnmanagedConsumption Channel to predictor for TimeOfUseTariff Apps

* GoodWe 20: fix Grid Meter voltage and current

* EVCS: Improve "Chargingstation Communication Failed" message

Add a text for the evcs fault state in the common format.

* AppCenter: fixed "no" check validation before installation

- fix bug where a Home 10 and Home 30 could be installed at the same time because of not correctly checking the installation/compatibility checks
 - also added check for the configuration to be successful
 - fixed FixActivePower/PrepareBatteryExtension wrong property name
- added timeout to backend request of 30 seconds & filter for Backend componet to be enabled
- added DummyApp for tests

* Home Battery: set SerialNumber prefix depending on the hardware type

* Home Battery: modify PolyLine for 64Ah battery

* GoodWe: set `GoodWeType` from type register or serial number

* Time-Of-Use Tariff Charge: base

* Time-of-Use: implement Optimizer based on Jenetics

* AppCenter: load app images from external file server

* UI: show capacity extension mode for all roles

* AppCenter: Removed app images from edge

* AppCenter: delete EntsoE.png & MQTT image

* UI: Time of Use tariff Chart implementation in Live Widget

---------

Co-authored-by: Sebastian Asen <[email protected]>
Co-authored-by: Lukas Rieger <[email protected]>
Co-authored-by: Michael Grill <[email protected]>
Co-authored-by: Sagar Venu <[email protected]>
Co-authored-by: Stefan Feilmeier <[email protected]>
Co-authored-by: Hueseyin Sahutoglu <[email protected]>
  • Loading branch information
7 people authored Nov 14, 2023
1 parent e0cb25a commit ee11345
Show file tree
Hide file tree
Showing 130 changed files with 6,434 additions and 24,627 deletions.
7 changes: 7 additions & 0 deletions cnf/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@
<artifactId>rxjava</artifactId>
<version>3.1.8</version>
</dependency>
<dependency>
<!-- Jenetics - Java Genetic Algorithm Library -->
<!-- Release notes: https://github.com/jenetics/jenetics/blob/master/RELEASE_NOTES.md -->
<groupId>io.jenetics</groupId>
<artifactId>jenetics</artifactId>
<version>7.2.0</version>
</dependency>
<!-- javax -->
<dependency>
<groupId>javax.jmdns</groupId>
Expand Down
12 changes: 12 additions & 0 deletions io.openems.common/src/io/openems/common/utils/DateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.function.BiFunction;

import io.openems.common.exceptions.OpenemsException;
Expand All @@ -26,6 +27,17 @@ public class DateUtils {
private DateUtils() {
}

/**
* Rounds a {@link ZonedDateTime} down to given minutes.
*
* @param d the {@link ZonedDateTime}
* @param minutes the minutes to round down to; max 59
* @return the rounded result
*/
public static ZonedDateTime roundZonedDateTimeDownToMinutes(ZonedDateTime d, int minutes) {
return d.withMinute(d.getMinute() - d.getMinute() % minutes).truncatedTo(ChronoUnit.MINUTES);
}

/**
* Asserts that both dates are in the same timezone.
*
Expand Down
16 changes: 16 additions & 0 deletions io.openems.common/test/io/openems/common/utils/DateUtilsTest.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
package io.openems.common.utils;

import static io.openems.common.utils.DateUtils.roundZonedDateTimeDownToMinutes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

import org.junit.Test;

import io.openems.common.exceptions.OpenemsException;

public class DateUtilsTest {

@Test
public void testRoundZonedDateTimeDownToMinutes() throws Exception {
assertEquals(//
ZonedDateTime.of(2023, 1, 2, 3, 0, 0, 0, ZoneId.of("UTC")), //
roundZonedDateTimeDownToMinutes(//
ZonedDateTime.of(2023, 1, 2, 3, 4, 5, 6, ZoneId.of("UTC")), 15));

assertEquals(//
ZonedDateTime.of(2023, 1, 2, 3, 15, 0, 0, ZoneId.of("UTC")), //
roundZonedDateTimeDownToMinutes(//
ZonedDateTime.of(2023, 1, 2, 3, 16, 17, 18, ZoneId.of("UTC")), 15));
}

@Test
public void testParseDateWithDmyFormat() throws Exception {
var dateString = "11.11.2018";
Expand Down
1 change: 1 addition & 0 deletions io.openems.edge.application/EdgeApp.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@
io.openems.wrapper.influxdb-flux-dsl;version=snapshot,\
io.openems.wrapper.io.reactivex.rxjava3.rxjava;version=snapshot,\
io.openems.wrapper.javax.activation;version=snapshot,\
io.openems.wrapper.jenetics;version=snapshot,\
io.openems.wrapper.kotlinx-coroutines-core-jvm;version=snapshot,\
io.openems.wrapper.okhttp;version=snapshot,\
io.openems.wrapper.opczip;version=snapshot,\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

public enum BatteryFeneconHomeHardwareType implements OptionsEnum {

BATTERY_52(52, "Fenecon Home Battery 52Ah", 2200, 42, 49, 14, 3, new FeneconHomeBatteryProtection52()), //
BATTERY_64(64, "Fenecon Home Battery 64,4Ah", 2800, 40.6f, 49.7f, 14, 5, new FeneconHomeBatteryProtection64()); //
BATTERY_52(52, "Fenecon Home Battery 52Ah", 2200, 42, 49, 14, 3, new FeneconHomeBatteryProtection52(),
"519100001009", "519110001210"), //
BATTERY_64(64, "Fenecon Home Battery 64,4Ah", 2800, 40.6f, 49.7f, 14, 5, new FeneconHomeBatteryProtection64(),
"519100001254", "519110001918"); //

/**
* Defaults to {@link #BATTERY_52} to avoid detection failure with old firmware
Expand All @@ -20,13 +22,15 @@ public enum BatteryFeneconHomeHardwareType implements OptionsEnum {
public final int cellsPerModule;
public final int tempSensorsPerModule;
public final BatteryProtectionDefinition batteryProtection;
public final String serialNrPrefixBms;
public final String serialNrPrefixModule;

private final int value;
private final String type;

private BatteryFeneconHomeHardwareType(int value, String type, int capacityPerModule, float moduleMinVoltage,
float moduleMaxVoltage, int cellsPerModule, int tempSensorsPerModule,
BatteryProtectionDefinition batteryProtection) {
BatteryProtectionDefinition batteryProtection, String serialNrPrefixBms, String serialNrPrefixModule) {
this.value = value;
this.type = type;
this.capacityPerModule = capacityPerModule;
Expand All @@ -35,6 +39,8 @@ private BatteryFeneconHomeHardwareType(int value, String type, int capacityPerMo
this.cellsPerModule = cellsPerModule;
this.tempSensorsPerModule = tempSensorsPerModule;
this.batteryProtection = batteryProtection;
this.serialNrPrefixBms = serialNrPrefixBms;
this.serialNrPrefixModule = serialNrPrefixModule;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@
public class BatteryFeneconHomeImpl extends AbstractOpenemsModbusComponent implements ModbusComponent, OpenemsComponent,
Battery, EventHandler, ModbusSlave, StartStoppable, BatteryFeneconHome {

private static final String SERIAL_NUMBER_PREFIX_BMS = "519100001009";
private static final String SERIAL_NUMBER_PREFIX_MODULE = "519110001210";

protected final StateMachine stateMachine = new StateMachine(State.UNDEFINED);

private final Logger log = LoggerFactory.getLogger(BatteryFeneconHomeImpl.class);
Expand Down Expand Up @@ -762,7 +759,7 @@ private synchronized void initializeTowerModulesChannels(int numberOfTowers, int
new UnsignedDoublewordElement(towerOffset + 51),
new ElementToChannelConverter(value -> {
Integer intValue = TypeUtils.getAsType(OpenemsType.INTEGER, value);
return buildSerialNumber(SERIAL_NUMBER_PREFIX_BMS, intValue);
return buildSerialNumber(this.getBatteryHardwareType().serialNrPrefixBms, intValue);
}))));
}

Expand Down Expand Up @@ -854,7 +851,7 @@ private synchronized void initializeTowerModulesChannels(int numberOfTowers, int
m(channelId, new UnsignedDoublewordElement(moduleOffset + module * 100 + 83),
new ElementToChannelConverter(value -> {
Integer intValue = TypeUtils.getAsType(OpenemsType.INTEGER, value);
return buildSerialNumber(SERIAL_NUMBER_PREFIX_MODULE, intValue);
return buildSerialNumber(this.getBatteryHardwareType().serialNrPrefixModule, intValue);
}))));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public int getInitialBmsMaxEverDischargeCurrent() {
public PolyLine getChargeVoltageToPercent() {
return PolyLine.create() //
.addPoint(2000, 0.1) //
.addPoint(3000, 0.1) //
.addPoint(3000, 0.2) //
.addPoint(Math.nextUp(3000), 1) //
.addPoint(3450, 1) //
.addPoint(3540, 0.08) //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
import io.openems.common.types.OptionsEnum;

public enum Currency implements OptionsEnum {
UNDEFINED(-1, "-"), //
EUR(0, "€"), //
SEK(1, "kr"), //
UNDEFINED(-1), //
EUR(0), //
SEK(1), //
;

private final String name;
private final int value;

private Currency(int value, String name) {
private Currency(int value) {
this.value = value;
this.name = name;
}

@Override
Expand All @@ -23,7 +21,7 @@ public int getValue() {

@Override
public String getName() {
return this.name;
return this.name();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public default EnumReadChannel getCurrencyChannel() {
}

/**
* Gets the Capacity in [Wh]. See {@link ChannelId#CURRENCY}.
* Gets the Currency. See {@link ChannelId#CURRENCY}.
*
* @return the Channel {@link Value}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.openems.edge.common.sum;

import static io.openems.edge.common.test.TestUtils.withValue;

import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.test.AbstractDummyOpenemsComponent;

Expand All @@ -25,4 +27,26 @@ public void updateChannelsBeforeProcessImage() {
// nothing here
}

/**
* Set {@link Sum.ChannelId#PRODUCTION_AC_ACTIVE_POWER}.
*
* @param value the value
* @return myself
*/
public DummySum withProductionAcActivePower(int value) {
withValue(this, Sum.ChannelId.PRODUCTION_AC_ACTIVE_POWER, value);
return this.self();
}

/**
* Set {@link Sum.ChannelId#GRID_ACTIVE_POWER}.
*
* @param value the value
* @return myself
*/
public DummySum withGridActivePower(int value) {
withValue(this, Sum.ChannelId.GRID_ACTIVE_POWER, value);
return this.self();
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package io.openems.edge.controller.ess.emergencycapacityreserve.statemachine;

import static io.openems.edge.common.type.TypeUtils.max;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.edge.common.statemachine.StateHandler;
import io.openems.edge.common.sum.Sum;
import io.openems.edge.controller.ess.emergencycapacityreserve.statemachine.StateMachine.State;

public class ForceChargeHandler extends StateHandler<State, Context> {
Expand All @@ -11,8 +14,7 @@ protected State runAndGetNextState(Context context) throws OpenemsNamedException
var sum = context.sum;

// calculate target and ramp power
int acProduction = sum.getProductionAcActivePower().orElse(0);
context.setTargetPower(acProduction * -1);
context.setTargetPower(getAcPvProduction(sum) * -1);
context.setRampPower(context.maxApparentPower * 0.01);

var reserveSoc = context.reserveSoc;
Expand All @@ -26,4 +28,14 @@ protected State runAndGetNextState(Context context) throws OpenemsNamedException
return State.FORCE_CHARGE;
}

/**
* Gets AC-PV Production.
*
* @param sum the {@link Sum}
* @return the AC-PV Production, always >= 0
*/
protected static int getAcPvProduction(Sum sum) {
return max(sum.getProductionAcActivePower().get(), 0);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.openems.edge.controller.ess.emergencycapacityreserve.statemachine;

import static io.openems.edge.controller.ess.emergencycapacityreserve.statemachine.ForceChargeHandler.getAcPvProduction;
import static org.junit.Assert.assertEquals;

import org.junit.Test;

import io.openems.edge.common.sum.DummySum;

public class ForceChargeHandlerTest {

@Test
public void testGetAcPvProduction() {
var sum = new DummySum();

// Fallback to 'zero' for null
assertEquals(0, getAcPvProduction(sum));

// Guarantee positive values
assertEquals(0, getAcPvProduction(sum.withProductionAcActivePower(-100)));

// Get positive values
assertEquals(1234, getAcPvProduction(sum.withProductionAcActivePower(1234)));
}

}
1 change: 1 addition & 0 deletions io.openems.edge.controller.ess.timeofusetariff/bnd.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Bundle-Version: 1.0.0.${tstamp}
io.openems.edge.predictor.api,\
io.openems.edge.timedata.api,\
io.openems.edge.timeofusetariff.api,\
io.openems.wrapper.jenetics,\

-testpath: \
${testpath}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@

public enum ControlMode {
/**
* Charge consumption from the grid.
* Delays discharge during low-price hours.
*/
CHARGE_CONSUMPTION, //
DELAY_DISCHARGE(//
StateMachine.BALANCING, //
StateMachine.DELAY_DISCHARGE //
),
/**
* Delays discharge during low-price hours.
* Active Charge from grid.
*/
DELAY_DISCHARGE; //
CHARGE_CONSUMPTION(//
StateMachine.BALANCING, //
StateMachine.DELAY_DISCHARGE, //
StateMachine.CHARGE //
);

public final StateMachine[] states;

private ControlMode(StateMachine... states) {
this.states = states;
}
}
Loading

0 comments on commit ee11345

Please sign in to comment.