Skip to content

Commit

Permalink
ToU+EVCS: fix calculation of _sum/UnmanagedConsumptionActivePower (#…
Browse files Browse the repository at this point in the history
…2856)

- Add MeterType.MANAGED_CONSUMPTION_METERED
  - Distinguish between managed and unmanaged consumption power
  - This will also be used for ReadOnly-EVCS
- Fix calculation of `_sum/UnmanagedConsumptionActivePower`
- Fixing this is critical, because its used to calculate required Consumption Energy in Time-of-Use-Tariff-Controller
- Move `MeterType` to `io.openems.common`
- UI History chart Y-Axis Unit symbol instead of string
  - Show "%" instead of "percentage", etc.
  - Add empty line to avoid resorting of imports (``uuid` import should occur before import of `src/app/edge/history/shared`eslintimport/order`)
  • Loading branch information
sfeilmeier authored Oct 22, 2024
1 parent 34b5e3a commit 37bd288
Show file tree
Hide file tree
Showing 201 changed files with 506 additions and 261 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.openems.backend.common.jsonrpc.request.SimulationRequest;
import io.openems.backend.common.metadata.User;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.jsonrpc.base.JsonrpcRequest;
import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess;

public interface SimulationEngine {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.openems.common.types.ChannelAddress;
import io.openems.common.types.CurrencyConfig;
import io.openems.common.types.EdgeConfig;
import io.openems.common.types.MeterType;
import io.openems.common.utils.JsonUtils;

public class XlsxExportUtil {
Expand All @@ -32,11 +33,11 @@ private static CurrencyConfig getCurrency(EdgeConfig edge) throws OpenemsNamedEx
/**
* Gathers the detail data for excel export.
*
* @param edge the edge
* @return the detailData
* @throws OpenemsNamedException if component isnt found
* @param edgeConfig the {@link EdgeConfig}
* @return the {@link XlsxExportDetailData}
* @throws OpenemsNamedException if component is not found
*/
public static XlsxExportDetailData getDetailData(EdgeConfig edge) throws OpenemsNamedException {
public static XlsxExportDetailData getDetailData(EdgeConfig edgeConfig) throws OpenemsNamedException {
final var enumMap = new EnumMap<XlsxExportCategory, List<XlsxExportDataEntry>>(XlsxExportCategory.class);
final var consumption = new ArrayList<XlsxExportDetailData.XlsxExportDataEntry>();
final var production = new ArrayList<XlsxExportDetailData.XlsxExportDataEntry>();
Expand All @@ -46,31 +47,37 @@ public static XlsxExportDetailData getDetailData(EdgeConfig edge) throws Openems
enumMap.put(XlsxExportCategory.CONSUMPTION, consumption);
enumMap.put(XlsxExportCategory.TIME_OF_USE_TARIFF, tou);

for (var component : edge.getComponents().values()) {
final var natures = edge.getFactories().get(component.getFactoryId()).getNatureIds();
for (var nature : natures) {
for (var component : edgeConfig.getComponents().values()) {
final var factory = edgeConfig.getFactories().get(component.getFactoryId());
if (factory == null) {
continue;
}
for (var nature : factory.getNatureIds()) {
// Electricity meter
switch (nature) {
case Natures.METER -> {
final var props = component.getProperties();
if (props.keySet().contains("type")) {
if (props.get("type").getAsString().equals("PRODUCTION")) {
production.add(new XlsxExportDataEntry(component.getAlias(),
new ChannelAddress(component.getId(), "ActivePower"),
XlsxExportDataEntry.HistoricTimedataSaveType.POWER));
} else if (props.get("type").getAsString().equals("CONSUMPTION_NOT_METERED")
|| props.get("type").getAsString().equals("CONSUMPTION_METERED")) {
consumption.add(new XlsxExportDataEntry(component.getAlias(),
var meterType = JsonUtils.<MeterType>getAsOptionalEnum(MeterType.class, props.get("type"))
.orElse(null);
if (meterType != null) {
var list = switch (meterType) {
case CONSUMPTION_METERED, CONSUMPTION_NOT_METERED, MANAGED_CONSUMPTION_METERED -> consumption;
case PRODUCTION -> production;
case GRID, PRODUCTION_AND_CONSUMPTION -> null;
};
if (list != null) {
list.add(new XlsxExportDataEntry(component.getAlias(),
new ChannelAddress(component.getId(), "ActivePower"),
XlsxExportDataEntry.HistoricTimedataSaveType.POWER));
}
continue;
}
final var type = getActivePowerType(component.getFactoryId());
if (type == null) {

final var activePowerType = getActivePowerType(component.getFactoryId());
if (activePowerType == null) {
continue;
}
enumMap.get(type)
enumMap.get(activePowerType)
.add(new XlsxExportDataEntry(component.getAlias(),
new ChannelAddress(component.getId(), "ActivePower"),
XlsxExportDataEntry.HistoricTimedataSaveType.POWER));
Expand All @@ -82,7 +89,7 @@ public static XlsxExportDetailData getDetailData(EdgeConfig edge) throws Openems
}
}
}
return new XlsxExportDetailData(enumMap, XlsxExportUtil.getCurrency(edge));
return new XlsxExportDetailData(enumMap, XlsxExportUtil.getCurrency(edgeConfig));
}

private static XlsxExportCategory getActivePowerType(String factoryId) {
Expand All @@ -106,7 +113,7 @@ private static final class Natures {
public static final Set<String> CONSUMPTION_NATURES = Set.of("GoodWe.EmergencyPowerMeter",
"Simulator.NRCMeter.Acting", "Evcs.AlpitronicHypercharger", "Evcs.Dezony", "Evcs.Goe.ChargerHome",
"Evcs.HardyBarth", "Evcs.Keba.KeContact", "Evcs.Ocpp.Abl", "Evcs.Ocpp.IesKeywattSingle",
"Evcs.Spelsberg.SMART", "Evcs.Webasto.Next","Evcs.Webasto.Unite");
"Evcs.Spelsberg.SMART", "Evcs.Webasto.Next", "Evcs.Webasto.Unite");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ public static ActualEdgeConfig empty() {
}

/**
* Create a {@link ActualEdgeConfig.Builder} builder.
* Create a ActualEdgeConfig builder.
*
* @return a {@link Builder}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package io.openems.edge.meter.api;
package io.openems.common.types;

/**
* Defines the type of the Meter.
* Defines the type of an ElectricityMeter.
*
* <p>
* See "io.openems.edge.meter.api" for details.
*/
public enum MeterType {
/**
* Defines a Grid-Meter, i.e. a meter that is measuring at the grid connection
* point (German: "Netzanschlusspunkt")
*/
GRID, //
GRID,
/**
* Defines a Production-Meter, i.e. a meter that is measuring an electric
* producer like a photovoltaics installation
Expand All @@ -21,8 +24,12 @@ public enum MeterType {
*/
PRODUCTION_AND_CONSUMPTION,
/**
* Defines a Consumption-Meter that is metered, i.e. a meter that is measuring
* an electric consumer like a heating-element or electric car.
* Defines a Consumption-Meter that metered, i.e. a meter that is measuring an
* electric consumer like a heating-element or electric car.
*
* <p>
* Select this {@link MeterType} if the device is not actively managed by
* OpenEMS - see {@link #MANAGED_CONSUMPTION_METERED} otherwise.
*
* <p>
* Note: Consumption is generally calculated using the data from Grid-Meter,
Expand All @@ -31,6 +38,11 @@ public enum MeterType {
* expected to be already measured by the Grid-Meter.
*/
CONSUMPTION_METERED,
/**
* Defines a Consumption-Meter that is actively managed by OpenEMS and metered
* (See {@link #CONSUMPTION_METERED}).
*/
MANAGED_CONSUMPTION_METERED,
/**
* Defines a Consumption-Meter that is NOT metered, i.e. a meter that is
* measuring an electric consumer like a heating-element or electric car.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package io.openems.common.timedata;

import static io.openems.common.utils.JsonUtils.toJson;
import static org.junit.Assert.assertEquals;

import org.junit.Test;

import com.google.common.collect.ImmutableSortedMap;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.timedata.XlsxExportDetailData.XlsxExportCategory;
import io.openems.common.timedata.XlsxExportDetailData.XlsxExportDataEntry.HistoricTimedataSaveType;
import io.openems.common.types.EdgeConfig.ActualEdgeConfig;
import io.openems.common.types.EdgeConfig.Component;
import io.openems.common.types.EdgeConfig.Factory;
import io.openems.common.types.EdgeConfig.Factory.Property;

public class XlsxExportUtilTest {

@Test
public void testGetDetailData() throws OpenemsNamedException {
var edgeConfig = ActualEdgeConfig.create() //
.addComponent("meter0",
new Component("meter0", "My CONSUMPTION_METERED Meter", "Meter.Socomec.Threephase",
// Properties
ImmutableSortedMap.of("type", toJson("CONSUMPTION_METERED")),
// Channels
ImmutableSortedMap.of())) //
.addComponent("meter1",
new Component("meter1", "My CONSUMPTION_NOT_METERED Meter", "Meter.Socomec.Threephase",
// Properties
ImmutableSortedMap.of("type", toJson("CONSUMPTION_NOT_METERED")),
// Channels
ImmutableSortedMap.of())) //
.addComponent("meter2", new Component("meter2", "My PRODUCTION Meter", "Meter.Socomec.Threephase",
// Properties
ImmutableSortedMap.of("type", toJson("PRODUCTION")),
// Channels
ImmutableSortedMap.of())) //
.addComponent("meter3",
new Component("meter3", "My MANAGED_CONSUMPTION_METERED Meter", "Meter.Socomec.Threephase",
// Properties
ImmutableSortedMap.of("type", toJson("MANAGED_CONSUMPTION_METERED")),
// Channels
ImmutableSortedMap.of())) //

.addFactory("Meter.Socomec.Threephase",
new Factory("Meter.Socomec.Threephase", "My Name", "My Description", //
new Property[] {}, //
// Natures
new String[] { "io.openems.edge.meter.api.ElectricityMeter" })) //
.buildEdgeConfig();

final var result = XlsxExportUtil.getDetailData(edgeConfig);

var consumptions = result.data().get(XlsxExportCategory.CONSUMPTION);
assertEquals(3, consumptions.size());

{
var meter = consumptions.get(0);
assertEquals("My CONSUMPTION_METERED Meter", meter.alias());
assertEquals("meter0/ActivePower", meter.channel().toString());
assertEquals(HistoricTimedataSaveType.POWER, meter.type());
}
{
var meter = consumptions.get(1);
assertEquals("My CONSUMPTION_NOT_METERED Meter", meter.alias());
assertEquals("meter1/ActivePower", meter.channel().toString());
assertEquals(HistoricTimedataSaveType.POWER, meter.type());
}
{
var meter = consumptions.get(2);
assertEquals("My MANAGED_CONSUMPTION_METERED Meter", meter.alias());
assertEquals("meter3/ActivePower", meter.channel().toString());
assertEquals(HistoricTimedataSaveType.POWER, meter.type());
}

var productions = result.data().get(XlsxExportCategory.PRODUCTION);
assertEquals(1, productions.size());

{
var meter = productions.get(0);
assertEquals("My PRODUCTION Meter", meter.alias());
assertEquals("meter2/ActivePower", meter.channel().toString());
assertEquals(HistoricTimedataSaveType.POWER, meter.type());
}

var touts = result.data().get(XlsxExportCategory.TIME_OF_USE_TARIFF);
assertEquals(0, touts.size());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
import org.osgi.service.metatype.annotations.Designate;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.types.MeterType;
import io.openems.edge.bosch.bpts5hybrid.core.BoschBpts5HybridCore;
import io.openems.edge.common.channel.Doc;
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.meter.api.ElectricityMeter;
import io.openems.edge.meter.api.MeterType;

@Designate(ocd = Config.class, factory = true)
@Component(//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.junit.Test;

import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyConfigurationAdmin;

Expand All @@ -21,6 +22,7 @@ public void test() throws Exception {
.setIpaddress("127.0.0.1") //
.setInterval(2) //
.build()) //
;
.next(new TestCase()) //
.deactivate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.junit.Test;

import io.openems.edge.bosch.bpts5hybrid.core.BoschBpts5HybridCoreImpl;
import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyConfigurationAdmin;

Expand Down Expand Up @@ -30,6 +31,7 @@ public void test() throws Exception {
.setId(ESS_ID) //
.setCoreId(CORE_ID) //
.build()) //
;
.next(new TestCase()) //
.deactivate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.junit.Test;

import io.openems.edge.bosch.bpts5hybrid.core.BoschBpts5HybridCoreImpl;
import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyConfigurationAdmin;

Expand Down Expand Up @@ -30,6 +31,7 @@ public void test() throws Exception {
.setId(METER_ID) //
.setCoreId(CORE_ID) //
.build()) //
;
.next(new TestCase()) //
.deactivate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.junit.Test;

import io.openems.edge.bosch.bpts5hybrid.core.BoschBpts5HybridCoreImpl;
import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyConfigurationAdmin;

Expand Down Expand Up @@ -30,6 +31,7 @@ public void test() throws Exception {
.setId(CHARGER_ID) //
.setCoreId(CORE_ID) //
.build()) //
;
.next(new TestCase()) //
.deactivate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,18 @@

import org.junit.Test;

import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;

public class BridgeMbusImplTest {

private static final String COMPONENT_ID = "mbus0";

@Test
public void test() throws Exception {
new ComponentTest(new BridgeMbusImpl()) //
.activate(MyConfig.create() //
.setId(COMPONENT_ID) //
.setId("mbus0") //
.setPortName("/dev/ttyUSB0") //
.setBaudrate(2400) //
.build()) //
.next(new TestCase()); //
.build()); //
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.google.gson.JsonNull;
import com.google.gson.JsonPrimitive;

import io.openems.common.types.MeterType;
import io.openems.common.utils.JsonUtils;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.core.appmanager.AppDef;
Expand All @@ -31,7 +32,6 @@
import io.openems.edge.core.appmanager.formly.enums.DisplayType;
import io.openems.edge.ess.api.ManagedSymmetricEss;
import io.openems.edge.meter.api.ElectricityMeter;
import io.openems.edge.meter.api.MeterType;

/**
* Static method collection for {@link AppDef AppDefs} for selecting different
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import io.openems.edge.core.appmanager.TranslationUtil;

/**
* Copy of {@link io.openems.edge.meter.api.MeterType}.
* Copy of {@link io.openems.common.types.MeterType}.
*/
public enum MeterType implements TranslatableEnum {
PRODUCTION("App.Meter.production"), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ private Prediction getPredictionSum(Sum.ChannelId channelId) {
switch (meter.getMeterType()) {
case GRID:
case CONSUMPTION_METERED:
case MANAGED_CONSUMPTION_METERED:
case CONSUMPTION_NOT_METERED:
return false;
case PRODUCTION:
Expand Down
Loading

0 comments on commit 37bd288

Please sign in to comment.