diff --git a/cnf/checkstyle.xml b/cnf/checkstyle.xml index bd1b163addc..50a9e218fdf 100644 --- a/cnf/checkstyle.xml +++ b/cnf/checkstyle.xml @@ -201,7 +201,7 @@ - + diff --git a/doc/modules/ROOT/pages/single_document.adoc b/doc/modules/ROOT/pages/single_document.adoc index a1a1c8a9311..eb09fc6fec9 100644 --- a/doc/modules/ROOT/pages/single_document.adoc +++ b/doc/modules/ROOT/pages/single_document.adoc @@ -1,7 +1,7 @@ = OpenEMS - Open Energy Management System ifndef::toc[] (c) 2020 OpenEMS Association e.V. -Version 2021.4.0 +Version 2021.5.0 :sectnums: :sectnumlevels: 4 :toc: diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index 65864913ef6..9e1e7535fdc 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -6,11 +6,16 @@ -runproperties: \ org.osgi.service.http.port=8079,\ - felix.cm.dir=c:/openems-backend-config + felix.cm.dir=c:/openems-backend-config,\ + org.ops4j.pax.logging.DefaultServiceLog.level=INFO -runsystempackages: \ sun.misc +-runbundles+: \ + org.apache.felix.scr;startlevel=10,\ + org.ops4j.pax.logging.pax-logging-log4j1;startlevel=12 + -runrequires: \ bnd.identity;id='org.ops4j.pax.logging.pax-logging-api',\ bnd.identity;id='org.ops4j.pax.logging.pax-logging-log4j1',\ @@ -24,13 +29,13 @@ bnd.identity;id='io.openems.backend.b2bwebsocket',\ bnd.identity;id='io.openems.backend.common',\ bnd.identity;id='io.openems.backend.core',\ - bnd.identity;id='io.openems.backend.edgewebsocket.impl',\ + bnd.identity;id='io.openems.backend.edgewebsocket',\ bnd.identity;id='io.openems.backend.metadata.dummy',\ bnd.identity;id='io.openems.backend.metadata.file',\ bnd.identity;id='io.openems.backend.metadata.odoo',\ bnd.identity;id='io.openems.backend.timedata.dummy',\ bnd.identity;id='io.openems.backend.timedata.influx',\ - bnd.identity;id='io.openems.backend.uiwebsocket.impl',\ + bnd.identity;id='io.openems.backend.uiwebsocket',\ -runbundles: \ Java-WebSocket;version='[1.5.1,1.5.2)',\ @@ -43,17 +48,13 @@ io.openems.backend.b2bwebsocket;version=snapshot,\ io.openems.backend.common;version=snapshot,\ io.openems.backend.core;version=snapshot,\ - io.openems.backend.edgewebsocket.api;version=snapshot,\ - io.openems.backend.edgewebsocket.impl;version=snapshot,\ - io.openems.backend.metadata.api;version=snapshot,\ + io.openems.backend.edgewebsocket;version=snapshot,\ io.openems.backend.metadata.dummy;version=snapshot,\ io.openems.backend.metadata.file;version=snapshot,\ io.openems.backend.metadata.odoo;version=snapshot,\ - io.openems.backend.timedata.api;version=snapshot,\ io.openems.backend.timedata.dummy;version=snapshot,\ io.openems.backend.timedata.influx;version=snapshot,\ - io.openems.backend.uiwebsocket.api;version=snapshot,\ - io.openems.backend.uiwebsocket.impl;version=snapshot,\ + io.openems.backend.uiwebsocket;version=snapshot,\ io.openems.common;version=snapshot,\ io.openems.shared.influxdb;version=snapshot,\ io.openems.wrapper.fastexcel;version=snapshot,\ diff --git a/io.openems.backend.application/bnd.bnd b/io.openems.backend.application/bnd.bnd index cd91cfe2528..be4c34c387a 100644 --- a/io.openems.backend.application/bnd.bnd +++ b/io.openems.backend.application/bnd.bnd @@ -6,12 +6,8 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.edgewebsocket.api,\ - io.openems.backend.metadata.api,\ - io.openems.backend.timedata.api,\ - io.openems.backend.uiwebsocket.api,\ io.openems.common,\ - Java-WebSocket;version=1.4.1 + Java-WebSocket;version='1.4.1' -testpath: \ ${testpath} diff --git a/io.openems.backend.b2brest/bnd.bnd b/io.openems.backend.b2brest/bnd.bnd index 870f5964a35..7f877eb3902 100644 --- a/io.openems.backend.b2brest/bnd.bnd +++ b/io.openems.backend.b2brest/bnd.bnd @@ -6,9 +6,8 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.metadata.api,\ io.openems.common,\ - Java-WebSocket;version=1.4.1,\ + Java-WebSocket;version='1.4.1',\ javax.servlet-api,\ org.apache.felix.http.jetty diff --git a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/B2bRest.java b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/B2bRest.java index 1b651fd199b..fd165f6c747 100644 --- a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/B2bRest.java +++ b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/B2bRest.java @@ -14,7 +14,7 @@ import io.openems.backend.common.component.AbstractOpenemsBackendComponent; import io.openems.backend.common.jsonrpc.JsonRpcRequestHandler; -import io.openems.backend.metadata.api.Metadata; +import io.openems.backend.common.metadata.Metadata; import io.openems.common.exceptions.OpenemsException; @Designate(ocd = Config.class, factory = true) diff --git a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java index 83ce0cdbd20..d7eb506855e 100644 --- a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java +++ b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java @@ -25,7 +25,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; diff --git a/io.openems.backend.b2bwebsocket/bnd.bnd b/io.openems.backend.b2bwebsocket/bnd.bnd index b7ffcf61ff4..0837f5adb4d 100644 --- a/io.openems.backend.b2bwebsocket/bnd.bnd +++ b/io.openems.backend.b2bwebsocket/bnd.bnd @@ -6,10 +6,8 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.metadata.api,\ - io.openems.backend.timedata.api,\ io.openems.common,\ - Java-WebSocket;version=1.4.1 + Java-WebSocket;version='1.4.1' -testpath: \ ${testpath} diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/B2bWebsocket.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/B2bWebsocket.java index 1f7ad4fc13c..d5486dcbc73 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/B2bWebsocket.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/B2bWebsocket.java @@ -12,8 +12,8 @@ import io.openems.backend.common.component.AbstractOpenemsBackendComponent; import io.openems.backend.common.jsonrpc.JsonRpcRequestHandler; -import io.openems.backend.metadata.api.Metadata; -import io.openems.backend.timedata.api.Timedata; +import io.openems.backend.common.metadata.Metadata; +import io.openems.backend.common.timedata.Timedata; @Designate(ocd = Config.class, factory = true) @Component(// @@ -40,23 +40,33 @@ public B2bWebsocket() { super("Backend2Backend.Websocket"); } + private Config config; + + private final Runnable startServerWhenMetadataIsInitialized = () -> { + this.startServer(config.port(), config.poolSize(), config.debugMode()); + }; + @Activate void activate(Config config) { - this.startServer(config.port()); + this.config = config; + this.metadata.addOnIsInitializedListener(this.startServerWhenMetadataIsInitialized); } @Deactivate void deactivate() { + this.metadata.removeOnIsInitializedListener(this.startServerWhenMetadataIsInitialized); this.stopServer(); } /** * Create and start new server. * - * @param port the port + * @param port the port + * @param poolSize number of threads dedicated to handle the tasks + * @param debugMode activate a regular debug log about the state of the tasks */ - private synchronized void startServer(int port) { - this.server = new WebsocketServer(this, this.getName(), port); + private synchronized void startServer(int port, int poolSize, boolean debugMode) { + this.server = new WebsocketServer(this, this.getName(), port, poolSize, debugMode); this.server.start(); } diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/Config.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/Config.java index 958bd262292..fc7170bcb31 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/Config.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/Config.java @@ -11,6 +11,12 @@ @AttributeDefinition(name = "Port", description = "The port of the websocket server.") int port() default B2bWebsocket.DEFAULT_PORT; + @AttributeDefinition(name = "Number of Threads", description = "Pool-Size: the number of threads dedicated to handle the tasks") + int poolSize() default 10; + + @AttributeDefinition(name = "Debug Mode", description = "Activates the debug mode") + boolean debugMode() default false; + String webconsole_configurationFactory_nameHint() default "Backend2Backend Websocket"; } \ No newline at end of file diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java index fce45b7118d..87e68d9f4a9 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnClose.java @@ -6,7 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsException; public class OnClose implements io.openems.common.websocket.OnClose { diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnOpen.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnOpen.java index abac5e4612b..3728d9967d0 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnOpen.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnOpen.java @@ -9,7 +9,7 @@ import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.utils.JsonUtils; diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java index c9b57687d32..f89dc831614 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/OnRequest.java @@ -7,7 +7,7 @@ import org.java_websocket.WebSocket; import io.openems.backend.b2bwebsocket.jsonrpc.request.SubscribeEdgesChannelsRequest; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java index 093a8369e2e..67864194c77 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/SubscribedEdgesChannelsWorker.java @@ -17,7 +17,7 @@ import io.openems.backend.b2bwebsocket.jsonrpc.notification.EdgesCurrentDataNotification; import io.openems.backend.b2bwebsocket.jsonrpc.request.SubscribeEdgesChannelsRequest; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.session.Role; import io.openems.common.types.ChannelAddress; diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WebsocketServer.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WebsocketServer.java index faec0bdf221..6fd6d5e62c6 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WebsocketServer.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WebsocketServer.java @@ -13,8 +13,8 @@ public class WebsocketServer extends AbstractWebsocketServer { private final OnError onError; private final OnClose onClose; - public WebsocketServer(B2bWebsocket parent, String name, int port) { - super(name, port); + public WebsocketServer(B2bWebsocket parent, String name, int port, int poolSize, boolean debugMode) { + super(name, port, poolSize, debugMode); this.parent = parent; this.onOpen = new OnOpen(parent); this.onRequest = new OnRequest(parent); diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java index e91ef514388..9d563d7b60f 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/WsData.java @@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; diff --git a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/SubscribeEdgesChannelsRequest.java b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/SubscribeEdgesChannelsRequest.java index 9cec97754a0..38ccd729fff 100644 --- a/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/SubscribeEdgesChannelsRequest.java +++ b/io.openems.backend.b2bwebsocket/src/io/openems/backend/b2bwebsocket/jsonrpc/request/SubscribeEdgesChannelsRequest.java @@ -1,7 +1,6 @@ package io.openems.backend.b2bwebsocket.jsonrpc.request; import java.util.TreeSet; -import java.util.UUID; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -36,7 +35,7 @@ public class SubscribeEdgesChannelsRequest extends JsonrpcRequest { public static SubscribeEdgesChannelsRequest from(JsonrpcRequest r) throws OpenemsNamedException { JsonObject p = r.getParams(); int count = JsonUtils.getAsInt(p, "count"); - SubscribeEdgesChannelsRequest result = new SubscribeEdgesChannelsRequest(r.getId(), count); + SubscribeEdgesChannelsRequest result = new SubscribeEdgesChannelsRequest(r, count); JsonArray edgeIds = JsonUtils.getAsJsonArray(p, "ids"); for (JsonElement edgeId : edgeIds) { result.addEdgeId(JsonUtils.getAsString(edgeId)); @@ -57,13 +56,14 @@ public static SubscribeEdgesChannelsRequest from(JsonObject j) throws OpenemsNam private final TreeSet edgeIds = new TreeSet<>(); private final TreeSet channels = new TreeSet<>(); - public SubscribeEdgesChannelsRequest(UUID id, int count) { - super(id, METHOD); + private SubscribeEdgesChannelsRequest(JsonrpcRequest request, int count) { + super(request, METHOD); this.count = count; } public SubscribeEdgesChannelsRequest(int count) { - this(UUID.randomUUID(), count); + super(METHOD); + this.count = count; } public void addEdgeId(String edgeId) { diff --git a/io.openems.backend.common/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.common/.settings/org.eclipse.core.resources.prefs index f346f2736a4..d5416543672 100644 --- a/io.openems.backend.common/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.common/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,7 @@ eclipse.preferences.version=1 +encoding//src/io/openems/backend/common/edgewebsocket/package-info.java=UTF-8 +encoding//src/io/openems/backend/common/metadata/package-info.java=UTF-8 +encoding//src/io/openems/backend/common/timedata/package-info.java=UTF-8 +encoding//src/io/openems/backend/common/uiwebsocket/package-info.java=UTF-8 encoding//test/.gitignore=UTF-8 encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.common/bnd.bnd b/io.openems.backend.common/bnd.bnd index bfc13e74daa..ef6c446c014 100644 --- a/io.openems.backend.common/bnd.bnd +++ b/io.openems.backend.common/bnd.bnd @@ -5,7 +5,6 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ - io.openems.backend.metadata.api,\ io.openems.common -testpath: \ diff --git a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocket.java b/io.openems.backend.common/src/io/openems/backend/common/edgewebsocket/EdgeWebsocket.java similarity index 97% rename from io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocket.java rename to io.openems.backend.common/src/io/openems/backend/common/edgewebsocket/EdgeWebsocket.java index 51b4cf46cc4..736871ad227 100644 --- a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/EdgeWebsocket.java +++ b/io.openems.backend.common/src/io/openems/backend/common/edgewebsocket/EdgeWebsocket.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.api; +package io.openems.backend.common.edgewebsocket; import java.util.UUID; import java.util.concurrent.CompletableFuture; diff --git a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/edgewebsocket/package-info.java similarity index 63% rename from io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/package-info.java rename to io.openems.backend.common/src/io/openems/backend/common/edgewebsocket/package-info.java index 555c4e1f4e1..572e0ac4681 100644 --- a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/package-info.java +++ b/io.openems.backend.common/src/io/openems/backend/common/edgewebsocket/package-info.java @@ -1,3 +1,3 @@ @org.osgi.annotation.versioning.Version("1.0.0") @org.osgi.annotation.bundle.Export -package io.openems.backend.uiwebsocket.api; +package io.openems.backend.common.edgewebsocket; diff --git a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/JsonRpcRequestHandler.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/JsonRpcRequestHandler.java index 4f73590cf99..cd90e47d440 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/JsonRpcRequestHandler.java +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/JsonRpcRequestHandler.java @@ -2,7 +2,7 @@ import java.util.concurrent.CompletableFuture; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; diff --git a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesChannelsValuesRequest.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesChannelsValuesRequest.java index a17820842c8..f269612d9b9 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesChannelsValuesRequest.java +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesChannelsValuesRequest.java @@ -1,14 +1,12 @@ package io.openems.backend.common.jsonrpc.request; import java.util.TreeSet; -import java.util.UUID; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; @@ -30,9 +28,19 @@ */ public class GetEdgesChannelsValuesRequest extends JsonrpcRequest { + public static final String METHOD = "getEdgesChannelsValues"; + + /** + * Create {@link GetEdgesChannelsValuesRequest} from a template + * {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link GetEdgesChannelsValuesRequest} + * @throws OpenemsNamedException on parse error + */ public static GetEdgesChannelsValuesRequest from(JsonrpcRequest r) throws OpenemsNamedException { JsonObject p = r.getParams(); - GetEdgesChannelsValuesRequest result = new GetEdgesChannelsValuesRequest(r.getId()); + GetEdgesChannelsValuesRequest result = new GetEdgesChannelsValuesRequest(r); JsonArray edgeIds = JsonUtils.getAsJsonArray(p, "ids"); for (JsonElement edgeId : edgeIds) { result.addEdgeId(JsonUtils.getAsString(edgeId)); @@ -45,37 +53,51 @@ public static GetEdgesChannelsValuesRequest from(JsonrpcRequest r) throws Openem return result; } - public static GetEdgesChannelsValuesRequest from(JsonObject j) throws OpenemsNamedException { - return from(GenericJsonrpcRequest.from(j)); - } - - public static final String METHOD = "getEdgesChannelsValues"; - private final TreeSet edgeIds = new TreeSet<>(); private final TreeSet channels = new TreeSet<>(); public GetEdgesChannelsValuesRequest() { - this(UUID.randomUUID()); + super(METHOD); } - public GetEdgesChannelsValuesRequest(UUID id) { - super(id, METHOD); + private GetEdgesChannelsValuesRequest(JsonrpcRequest request) { + super(request, METHOD); } + /** + * Adds a Edge-ID. + * + * @param edgeId the Edge-ID + */ public void addEdgeId(String edgeId) { this.edgeIds.add(edgeId); } + /** + * Gets the Edge-IDs. + * + * @return set of Edge-IDs. + */ public TreeSet getEdgeIds() { - return edgeIds; + return this.edgeIds; } + /** + * Adds a {@link ChannelAddress}. + * + * @param address the {@link ChannelAddress} + */ public void addChannel(ChannelAddress address) { this.channels.add(address); } + /** + * Gets the {@link ChannelAddress}es. + * + * @return the {@link ChannelAddress}es + */ public TreeSet getChannels() { - return channels; + return this.channels; } @Override diff --git a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesStatusRequest.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesStatusRequest.java index 3805c0eedbb..e5f763aaa1c 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesStatusRequest.java +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/GetEdgesStatusRequest.java @@ -1,9 +1,8 @@ package io.openems.backend.common.jsonrpc.request; -import java.util.UUID; - import com.google.gson.JsonObject; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.JsonrpcRequest; @@ -21,18 +20,25 @@ */ public class GetEdgesStatusRequest extends JsonrpcRequest { + public static final String METHOD = "getEdgesStatus"; + + /** + * Create {@link GetEdgesStatusRequest} from a template {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link GetEdgesStatusRequest} + * @throws OpenemsNamedException on parse error + */ public static GetEdgesStatusRequest from(JsonrpcRequest r) throws OpenemsException { - return new GetEdgesStatusRequest(r.getId()); + return new GetEdgesStatusRequest(r); } - public static final String METHOD = "getEdgesStatus"; - public GetEdgesStatusRequest() { - this(UUID.randomUUID()); + super(METHOD); } - public GetEdgesStatusRequest(UUID id) { - super(id, METHOD); + private GetEdgesStatusRequest(JsonrpcRequest request) { + super(request, METHOD); } @Override diff --git a/io.openems.backend.common/src/io/openems/backend/common/metadata/AbstractMetadata.java b/io.openems.backend.common/src/io/openems/backend/common/metadata/AbstractMetadata.java new file mode 100644 index 00000000000..0fb4af2c6ff --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/metadata/AbstractMetadata.java @@ -0,0 +1,58 @@ +package io.openems.backend.common.metadata; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.openems.backend.common.component.AbstractOpenemsBackendComponent; + +public abstract class AbstractMetadata extends AbstractOpenemsBackendComponent implements Metadata { + + /** + * Initializes the AbstractMetadata. + * + * @param name a descriptive name for this component. Available via + * {@link #getName()} + */ + protected AbstractMetadata(String name) { + super(name); + } + + private final AtomicBoolean isInitialized = new AtomicBoolean(false); + private final Set onIsInitializedListeners = new HashSet<>(); + + /** + * Make sure to call this method once initialized!. + */ + protected void setInitialized() { + this.isInitialized.set(true); + synchronized (this.onIsInitializedListeners) { + for (Runnable callback : this.onIsInitializedListeners) { + callback.run(); + } + } + } + + @Override + public final boolean isInitialized() { + return this.isInitialized.get(); + } + + @Override + public final void addOnIsInitializedListener(Runnable callback) { + synchronized (this.onIsInitializedListeners) { + this.onIsInitializedListeners.add(callback); + } + // Run callback if I was already initialized + if (this.isInitialized.get()) { + callback.run(); + } + } + + @Override + public final void removeOnIsInitializedListener(Runnable callback) { + synchronized (this.onIsInitializedListeners) { + this.onIsInitializedListeners.remove(callback); + } + } +} diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/BackendUser.java b/io.openems.backend.common/src/io/openems/backend/common/metadata/BackendUser.java similarity index 76% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/BackendUser.java rename to io.openems.backend.common/src/io/openems/backend/common/metadata/BackendUser.java index 4c6f1498de0..77078dbd791 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/BackendUser.java +++ b/io.openems.backend.common/src/io/openems/backend/common/metadata/BackendUser.java @@ -1,12 +1,16 @@ -package io.openems.backend.metadata.api; +package io.openems.backend.common.metadata; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Optional; import java.util.TreeMap; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.shared.EdgeMetadata; import io.openems.common.session.Role; import io.openems.common.session.User; @@ -127,4 +131,32 @@ public User getAsCommonUser(String edgeId) throws OpenemsNamedException { } return new io.openems.common.session.User(this.id, this.name, thisRole); } + + /** + * Gets the Metadata information of the accessible Edges. + * + * @param metadataService a {@link Metadata} provider + * @return a list of {@link EdgeMetadata} + */ + public List getEdgeMetadatas(Metadata metadataService) { + List metadatas = new ArrayList<>(); + for (Entry edgeRole : this.getEdgeRoles().entrySet()) { + String edgeId = edgeRole.getKey(); + Role role = edgeRole.getValue(); + Optional edgeOpt = metadataService.getEdge(edgeId); + if (edgeOpt.isPresent()) { + Edge e = edgeOpt.get(); + metadatas.add(new EdgeMetadata(// + e.getId(), // Edge-ID + e.getComment(), // Comment + e.getProducttype(), // Product-Type + e.getVersion(), // Version + role, // Role + e.isOnline() // Online-State + )); + } + } + return metadatas; + } + } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java b/io.openems.backend.common/src/io/openems/backend/common/metadata/Edge.java similarity index 87% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java rename to io.openems.backend.common/src/io/openems/backend/common/metadata/Edge.java index 1ab0a0a267c..3f0bfd94f44 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Edge.java +++ b/io.openems.backend.common/src/io/openems/backend/common/metadata/Edge.java @@ -1,9 +1,8 @@ -package io.openems.backend.metadata.api; +package io.openems.backend.common.metadata; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.List; -import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; @@ -13,7 +12,7 @@ import com.google.common.base.Objects; import com.google.gson.JsonObject; -import io.openems.common.types.ChannelAddress; +import io.openems.common.channel.Level; import io.openems.common.types.EdgeConfig; import io.openems.common.types.SemanticVersion; import io.openems.common.utils.JsonUtils; @@ -31,17 +30,20 @@ public enum State { private State state; private SemanticVersion version; private String producttype; + private Level sumState; private EdgeConfig config; private ZonedDateTime lastMessage = null; private ZonedDateTime lastUpdate = null; private boolean isOnline = false; - public Edge(String id, String comment, State state, String version, String producttype, EdgeConfig config) { + public Edge(String id, String comment, State state, String version, String producttype, Level sumState, + EdgeConfig config) { this.id = id; this.comment = comment; this.state = state; this.version = SemanticVersion.fromStringOrZero(version); this.producttype = producttype; + this.sumState = sumState; this.config = config; } @@ -298,16 +300,40 @@ public synchronized void setProducttype(String producttype, boolean callListener } /* - * Component States + * Sum-State (value of channel "_sum/State"). */ - private final List>> onSetComponentStates = new CopyOnWriteArrayList<>(); + public Level getSumState() { + return this.sumState; + } + + private final List> onSetSumState = new CopyOnWriteArrayList<>(); + + public void onSetSumState(Consumer listener) { + this.onSetSumState.add(listener); + } - public void onSetComponentState(Consumer> listener) { - this.onSetComponentStates.add(listener); + /** + * Sets the sumState and calls the SetSumState-Listeners. + * + * @param sumState the sumState + */ + public synchronized void setSumState(Level sumState) { + this.setSumState(sumState, true); } - public synchronized void setComponentState(Map activeStateChannels) { - this.onSetComponentStates.forEach(listener -> listener.accept(activeStateChannels)); + /** + * Sets the version. + * + * @param sumState the sumState + * @param callListeners whether to call the SetSumState-Listeners + */ + public synchronized void setSumState(Level sumState, boolean callListeners) { + if (!Objects.equal(this.sumState, sumState)) { // on change + if (callListeners) { + this.onSetSumState.forEach(listener -> listener.accept(sumState)); + } + this.sumState = sumState; + } } } diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Metadata.java b/io.openems.backend.common/src/io/openems/backend/common/metadata/Metadata.java similarity index 95% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Metadata.java rename to io.openems.backend.common/src/io/openems/backend/common/metadata/Metadata.java index 0eaa7a36bf3..bf2bad8597a 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/Metadata.java +++ b/io.openems.backend.common/src/io/openems/backend/common/metadata/Metadata.java @@ -1,4 +1,4 @@ -package io.openems.backend.metadata.api; +package io.openems.backend.common.metadata; import java.util.Collection; import java.util.HashMap; @@ -34,6 +34,16 @@ public interface Metadata { */ public boolean isInitialized(); + /** + * See {@link #isInitialized()}. + */ + public void addOnIsInitializedListener(Runnable callback); + + /** + * See {@link #isInitialized()}. + */ + public void removeOnIsInitializedListener(Runnable callback); + /** * Authenticates a User without any information. * diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserEdgesInfo.java b/io.openems.backend.common/src/io/openems/backend/common/metadata/UserEdgesInfo.java similarity index 92% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserEdgesInfo.java rename to io.openems.backend.common/src/io/openems/backend/common/metadata/UserEdgesInfo.java index b7722454f92..03b5a4b167c 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/UserEdgesInfo.java +++ b/io.openems.backend.common/src/io/openems/backend/common/metadata/UserEdgesInfo.java @@ -1,4 +1,4 @@ -package io.openems.backend.metadata.api; +package io.openems.backend.common.metadata; import java.util.HashMap; import java.util.Map; diff --git a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/metadata/package-info.java similarity index 65% rename from io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/package-info.java rename to io.openems.backend.common/src/io/openems/backend/common/metadata/package-info.java index 6cf08813d46..8e3e7a100a3 100644 --- a/io.openems.backend.metadata.api/src/io/openems/backend/metadata/api/package-info.java +++ b/io.openems.backend.common/src/io/openems/backend/common/metadata/package-info.java @@ -1,3 +1,3 @@ @org.osgi.annotation.versioning.Version("1.0.0") @org.osgi.annotation.bundle.Export -package io.openems.backend.metadata.api; +package io.openems.backend.common.metadata; diff --git a/io.openems.backend.common/src/io/openems/backend/common/timedata/EdgeCache.java b/io.openems.backend.common/src/io/openems/backend/common/timedata/EdgeCache.java new file mode 100644 index 00000000000..1507dc88d0f --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/timedata/EdgeCache.java @@ -0,0 +1,87 @@ +package io.openems.backend.common.timedata; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.SortedMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; + +import io.openems.common.types.ChannelAddress; + +public class EdgeCache { + + private final Logger log = LoggerFactory.getLogger(EdgeCache.class); + + /** + * The Timestamp of the data in the Cache. + */ + private long cacheTimestamp = 0l; + + /** + * The Timestamp when the Cache was last applied to the incoming data. + */ + private long lastAppliedTimestamp = 0l; + + private final HashMap cacheData = new HashMap<>(); + + public synchronized final Optional getChannelValue(ChannelAddress address) { + return Optional.ofNullable(this.cacheData.get(address)); + } + + public synchronized void complementDataFromCache(String edgeId, + SortedMap> incomingDatas) { + for (Entry> entry : incomingDatas.entrySet()) { + Long incomingTimestamp = entry.getKey(); + Map incomingData = entry.getValue(); + + // Check if cache should be applied + if (incomingTimestamp < this.cacheTimestamp) { + // Incoming data is older than cache -> do not apply cache + + } else { + // Incoming data is more recent than cache + + if (incomingTimestamp > this.cacheTimestamp + 5 * 60 * 1000) { + // Cache is not anymore valid (elder than 5 minutes) + if (this.cacheTimestamp != 0L) { + this.log.info("Edge [" + edgeId + "]: invalidate cache. Incoming [" + + Instant.ofEpochMilli(incomingTimestamp) + "]. Cache [" + + Instant.ofEpochMilli(cacheTimestamp) + "]"); + } + // Clear Cache + this.cacheData.clear(); + + } else if (incomingTimestamp < this.lastAppliedTimestamp + 60 * 1000) { + // Apply Cache only once every minute to throttle writes + + } else { + // Apply Cache + + // cache is valid (not elder than 5 minutes) + this.lastAppliedTimestamp = incomingTimestamp; + for (Entry cacheEntry : this.cacheData.entrySet()) { + ChannelAddress channel = cacheEntry.getKey(); + // check if there is a current value for this timestamp + channel + if (!incomingData.containsKey(channel)) { + // if not -> add cache data to write data + incomingData.put(channel, cacheEntry.getValue()); + } + } + } + + // update cache + this.cacheTimestamp = incomingTimestamp; + for (Entry channelEntry : incomingData.entrySet()) { + this.cacheData.put(channelEntry.getKey(), channelEntry.getValue()); + } + } + } + } + +} diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/Timedata.java b/io.openems.backend.common/src/io/openems/backend/common/timedata/Timedata.java similarity index 82% rename from io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/Timedata.java rename to io.openems.backend.common/src/io/openems/backend/common/timedata/Timedata.java index f11eaa97c53..201bb8ec071 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/Timedata.java +++ b/io.openems.backend.common/src/io/openems/backend/common/timedata/Timedata.java @@ -1,4 +1,4 @@ -package io.openems.backend.timedata.api; +package io.openems.backend.common.timedata; import java.util.Optional; @@ -18,8 +18,8 @@ public interface Timedata extends CommonTimedataService { * Sends the data points to the Timedata service. * * @param edgeId The unique Edge-ID - * @param data Table of timestamp (epoch in seconds), Channel-Address and the - * Channel value as JsonElement. Sorted by timestamp. + * @param data Table of timestamp (epoch in milliseconds), Channel-Address and + * the Channel value as JsonElement. Sorted by timestamp. * @throws OpenemsException */ public void write(String edgeId, TreeBasedTable data) throws OpenemsException; diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/timedata/package-info.java similarity index 65% rename from io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/package-info.java rename to io.openems.backend.common/src/io/openems/backend/common/timedata/package-info.java index d86a687c368..9fab095fb54 100644 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/package-info.java +++ b/io.openems.backend.common/src/io/openems/backend/common/timedata/package-info.java @@ -1,3 +1,3 @@ @org.osgi.annotation.versioning.Version("1.0.0") @org.osgi.annotation.bundle.Export -package io.openems.backend.timedata.api; +package io.openems.backend.common.timedata; diff --git a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocket.java b/io.openems.backend.common/src/io/openems/backend/common/uiwebsocket/UiWebsocket.java similarity index 96% rename from io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocket.java rename to io.openems.backend.common/src/io/openems/backend/common/uiwebsocket/UiWebsocket.java index 1dcd9e8472c..c0429f75886 100644 --- a/io.openems.backend.uiwebsocket.api/src/io/openems/backend/uiwebsocket/api/UiWebsocket.java +++ b/io.openems.backend.common/src/io/openems/backend/common/uiwebsocket/UiWebsocket.java @@ -1,4 +1,4 @@ -package io.openems.backend.uiwebsocket.api; +package io.openems.backend.common.uiwebsocket; import java.util.UUID; import java.util.concurrent.CompletableFuture; diff --git a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/package-info.java b/io.openems.backend.common/src/io/openems/backend/common/uiwebsocket/package-info.java similarity index 64% rename from io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/package-info.java rename to io.openems.backend.common/src/io/openems/backend/common/uiwebsocket/package-info.java index 959f38a7be4..c016fd26b84 100644 --- a/io.openems.backend.edgewebsocket.api/src/io/openems/backend/edgewebsocket/api/package-info.java +++ b/io.openems.backend.common/src/io/openems/backend/common/uiwebsocket/package-info.java @@ -1,3 +1,3 @@ @org.osgi.annotation.versioning.Version("1.0.0") @org.osgi.annotation.bundle.Export -package io.openems.backend.edgewebsocket.api; +package io.openems.backend.common.uiwebsocket; diff --git a/io.openems.backend.core/bnd.bnd b/io.openems.backend.core/bnd.bnd index 0651556e4ee..522e616b5a2 100644 --- a/io.openems.backend.core/bnd.bnd +++ b/io.openems.backend.core/bnd.bnd @@ -7,9 +7,6 @@ Bundle-Description: Supportive services that are used throughout OpenEMS Backend -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.edgewebsocket.api,\ - io.openems.backend.metadata.api,\ - io.openems.backend.timedata.api,\ io.openems.common -testpath: \ diff --git a/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/EdgeRpcRequestHandler.java b/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/EdgeRpcRequestHandler.java index e98e859ee36..11d883d0885 100644 --- a/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/EdgeRpcRequestHandler.java +++ b/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/EdgeRpcRequestHandler.java @@ -8,7 +8,7 @@ import com.google.gson.JsonElement; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.JsonrpcRequest; diff --git a/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/JsonRpcRequestHandlerImpl.java b/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/JsonRpcRequestHandlerImpl.java index a526f11009d..4f14a9e1396 100644 --- a/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/JsonRpcRequestHandlerImpl.java +++ b/io.openems.backend.core/src/io/openems/backend/core/jsonrpcrequesthandler/JsonRpcRequestHandlerImpl.java @@ -18,17 +18,17 @@ import com.google.gson.JsonNull; import io.openems.backend.common.component.AbstractOpenemsBackendComponent; +import io.openems.backend.common.edgewebsocket.EdgeWebsocket; import io.openems.backend.common.jsonrpc.JsonRpcRequestHandler; import io.openems.backend.common.jsonrpc.request.GetEdgesChannelsValuesRequest; import io.openems.backend.common.jsonrpc.request.GetEdgesStatusRequest; import io.openems.backend.common.jsonrpc.response.GetEdgesChannelsValuesResponse; import io.openems.backend.common.jsonrpc.response.GetEdgesStatusResponse; import io.openems.backend.common.jsonrpc.response.GetEdgesStatusResponse.EdgeInfo; -import io.openems.backend.edgewebsocket.api.EdgeWebsocket; -import io.openems.backend.metadata.api.BackendUser; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Metadata; -import io.openems.backend.timedata.api.Timedata; +import io.openems.backend.common.metadata.BackendUser; +import io.openems.backend.common.metadata.Edge; +import io.openems.backend.common.metadata.Metadata; +import io.openems.backend.common.timedata.Timedata; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; diff --git a/io.openems.backend.edgewebsocket.api/.gitignore b/io.openems.backend.edgewebsocket.api/.gitignore deleted file mode 100644 index 6ef3b17057e..00000000000 --- a/io.openems.backend.edgewebsocket.api/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/bin/ -/bin_test/ -/generated/ -/generated/ diff --git a/io.openems.backend.edgewebsocket.api/.project b/io.openems.backend.edgewebsocket.api/.project deleted file mode 100644 index 48f802565d5..00000000000 --- a/io.openems.backend.edgewebsocket.api/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - io.openems.backend.edgewebsocket.api - - - - - - org.eclipse.jdt.core.javabuilder - - - - - bndtools.core.bndbuilder - - - - - - org.eclipse.jdt.core.javanature - bndtools.core.bndnature - - diff --git a/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 67526271246..00000000000 --- a/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/edgewebsocket/api/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.edgewebsocket.api/bnd.bnd b/io.openems.backend.edgewebsocket.api/bnd.bnd deleted file mode 100644 index ae4b5cc7a2e..00000000000 --- a/io.openems.backend.edgewebsocket.api/bnd.bnd +++ /dev/null @@ -1,11 +0,0 @@ -Bundle-Name: OpenEMS Backend EdgeWebsocket Api -Bundle-Vendor: FENECON GmbH -Bundle-License: https://opensource.org/licenses/EPL-2.0 -Bundle-Version: 1.0.0.${tstamp} - --buildpath: \ - ${buildpath},\ - io.openems.common - --testpath: \ - ${testpath} diff --git a/io.openems.backend.edgewebsocket.impl/.project b/io.openems.backend.edgewebsocket.impl/.project deleted file mode 100644 index 5dae714bea7..00000000000 --- a/io.openems.backend.edgewebsocket.impl/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - io.openems.backend.edgewebsocket.impl - - - - - - org.eclipse.jdt.core.javabuilder - - - - - bndtools.core.bndbuilder - - - - - - org.eclipse.jdt.core.javanature - bndtools.core.bndnature - - diff --git a/io.openems.backend.edgewebsocket.api/.classpath b/io.openems.backend.edgewebsocket/.classpath similarity index 100% rename from io.openems.backend.edgewebsocket.api/.classpath rename to io.openems.backend.edgewebsocket/.classpath diff --git a/io.openems.backend.edgewebsocket.impl/.gitignore b/io.openems.backend.edgewebsocket/.gitignore similarity index 100% rename from io.openems.backend.edgewebsocket.impl/.gitignore rename to io.openems.backend.edgewebsocket/.gitignore diff --git a/io.openems.backend.metadata.api/.project b/io.openems.backend.edgewebsocket/.project similarity index 91% rename from io.openems.backend.metadata.api/.project rename to io.openems.backend.edgewebsocket/.project index ee22104f472..11c198299f1 100644 --- a/io.openems.backend.metadata.api/.project +++ b/io.openems.backend.edgewebsocket/.project @@ -1,6 +1,6 @@ - io.openems.backend.metadata.api + io.openems.backend.edgewebsocket diff --git a/io.openems.backend.edgewebsocket.impl/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.edgewebsocket/.settings/org.eclipse.core.resources.prefs similarity index 100% rename from io.openems.backend.edgewebsocket.impl/.settings/org.eclipse.core.resources.prefs rename to io.openems.backend.edgewebsocket/.settings/org.eclipse.core.resources.prefs diff --git a/io.openems.backend.edgewebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.edgewebsocket/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.edgewebsocket.api/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.edgewebsocket/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.edgewebsocket.impl/bnd.bnd b/io.openems.backend.edgewebsocket/bnd.bnd similarity index 60% rename from io.openems.backend.edgewebsocket.impl/bnd.bnd rename to io.openems.backend.edgewebsocket/bnd.bnd index 58338425fe3..d5b4d76bebd 100644 --- a/io.openems.backend.edgewebsocket.impl/bnd.bnd +++ b/io.openems.backend.edgewebsocket/bnd.bnd @@ -6,12 +6,8 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.edgewebsocket.api,\ - io.openems.backend.metadata.api,\ - io.openems.backend.timedata.api,\ - io.openems.backend.uiwebsocket.api,\ io.openems.common,\ - Java-WebSocket;version=1.4.1 + Java-WebSocket;version='1.4.1' -testpath: \ ${testpath} diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/Config.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/Config.java similarity index 59% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/Config.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/Config.java index 848a25f310a..ee664891487 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/Config.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/Config.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @@ -11,6 +11,12 @@ @AttributeDefinition(name = "Port", description = "The port of the websocket server.") int port() default 8081; + @AttributeDefinition(name = "Number of Threads", description = "Pool-Size: the number of threads dedicated to handle the tasks") + int poolSize() default 10; + + @AttributeDefinition(name = "Debug Mode", description = "Activates the debug mode") + boolean debugMode() default false; + String webconsole_configurationFactory_nameHint() default "Edge Websocket"; } diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/EdgeWebsocketImpl.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/EdgeWebsocketImpl.java similarity index 84% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/EdgeWebsocketImpl.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/EdgeWebsocketImpl.java index b2f5963a9e9..e5d66d3f31e 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/EdgeWebsocketImpl.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/EdgeWebsocketImpl.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.util.Optional; import java.util.UUID; @@ -16,10 +16,10 @@ import org.slf4j.LoggerFactory; import io.openems.backend.common.component.AbstractOpenemsBackendComponent; -import io.openems.backend.edgewebsocket.api.EdgeWebsocket; -import io.openems.backend.metadata.api.Metadata; -import io.openems.backend.timedata.api.Timedata; -import io.openems.backend.uiwebsocket.api.UiWebsocket; +import io.openems.backend.common.edgewebsocket.EdgeWebsocket; +import io.openems.backend.common.metadata.Metadata; +import io.openems.backend.common.timedata.Timedata; +import io.openems.backend.common.uiwebsocket.UiWebsocket; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; @@ -56,23 +56,33 @@ public EdgeWebsocketImpl() { this.systemLogHandler = new SystemLogHandler(this); } + private Config config; + + private final Runnable startServerWhenMetadataIsInitialized = () -> { + this.startServer(config.port(), config.poolSize(), config.debugMode()); + }; + @Activate void activate(Config config) { - this.startServer(config.port()); + this.config = config; + this.metadata.addOnIsInitializedListener(this.startServerWhenMetadataIsInitialized); } @Deactivate void deactivate() { + this.metadata.removeOnIsInitializedListener(this.startServerWhenMetadataIsInitialized); this.stopServer(); } /** * Create and start new server. * - * @param port the port + * @param port the port + * @param poolSize number of threads dedicated to handle the tasks + * @param debugMode activate a regular debug log about the state of the tasks */ - private synchronized void startServer(int port) { - this.server = new WebsocketServer(this, this.getName(), port); + private synchronized void startServer(int port, int poolSize, boolean debugMode) { + this.server = new WebsocketServer(this, this.getName(), port, poolSize, debugMode); this.server.start(); } @@ -115,7 +125,7 @@ public CompletableFuture send(String edgeId, User user, AuthenticatedRpcResponse response = AuthenticatedRpcResponse.from(r); result.complete(response.getPayload()); } catch (OpenemsNamedException e) { - this.logError(this.log, e.getMessage()); + this.logError(this.log, e.getMessage()); result.completeExceptionally(e); } } else { diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnClose.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnClose.java similarity index 93% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnClose.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnClose.java index d73c8b3dbef..c181a15bb58 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnClose.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnClose.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.util.Optional; @@ -7,7 +7,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.metadata.api.Edge; +import io.openems.backend.common.metadata.Edge; import io.openems.common.exceptions.OpenemsException; public class OnClose implements io.openems.common.websocket.OnClose { diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnError.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnError.java similarity index 94% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnError.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnError.java index e47f99a79e8..94dbde7cb91 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnError.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnError.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.util.Optional; diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnNotification.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnNotification.java similarity index 77% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnNotification.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnNotification.java index 4c544a510cb..d0593cc3026 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnNotification.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnNotification.java @@ -1,7 +1,5 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; -import java.util.HashMap; -import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -11,11 +9,10 @@ import org.slf4j.LoggerFactory; import com.google.gson.JsonElement; -import com.google.gson.JsonNull; import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import io.openems.backend.metadata.api.Edge; +import io.openems.backend.common.metadata.Edge; +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.JsonrpcNotification; @@ -23,9 +20,6 @@ import io.openems.common.jsonrpc.notification.EdgeRpcNotification; import io.openems.common.jsonrpc.notification.SystemLogNotification; import io.openems.common.jsonrpc.notification.TimestampedDataNotification; -import io.openems.common.types.ChannelAddress; -import io.openems.common.types.EdgeConfig; -import io.openems.common.types.EdgeConfig.Component.Channel; import io.openems.common.types.SemanticVersion; import io.openems.common.utils.JsonUtils; @@ -91,7 +85,7 @@ private void handleEdgeConfigNotification(EdgeConfigNotification message, WsData // forward try { this.parent.uiWebsocket.send(edgeId, new EdgeRpcNotification(edgeId, message)); - } catch (OpenemsNamedException e) { + } catch (OpenemsNamedException | NullPointerException e) { this.parent.logWarn(this.log, "Unable to forward EdgeConfigNotification to UI: " + e.getMessage()); } } @@ -127,37 +121,16 @@ private void handleTimestampedDataNotification(TimestampedDataNotification messa } // set specific Edge values + if (data.has("_sum/State") && data.get("_sum/State").isJsonPrimitive()) { + Level sumState = Level.fromJson(data, "_sum/State").orElse(Level.FAULT); + edge.setSumState(sumState); + } + if (data.has("_meta/Version") && data.get("_meta/Version").isJsonPrimitive()) { String version = JsonUtils.getAsPrimitive(data, "_meta/Version").getAsString(); edge.setVersion(SemanticVersion.fromString(version)); } - // parse State-Channels - Map activeStateChannels = new HashMap<>(); - for (Entry dataEntry : data.entrySet()) { - JsonElement value = dataEntry.getValue(); - if (value == JsonNull.INSTANCE || !value.isJsonPrimitive()) { - // not active -> ignore - continue; - } - JsonPrimitive primitive = value.getAsJsonPrimitive(); - if (!primitive.isNumber()) { - // cannot be a StateChannel - continue; - } - Number number = primitive.getAsNumber(); - if (number.intValue() != 1) { - // not active -> ignore - continue; - } - - ChannelAddress channelAddress = ChannelAddress.fromString(dataEntry.getKey()); - Optional channel = edge.getConfig().getStateChannel(channelAddress); - if (channel.isPresent()) { - activeStateChannels.put(channelAddress, channel.get()); - } - } - edge.setComponentState(activeStateChannels); } } diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnOpen.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnOpen.java similarity index 96% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnOpen.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnOpen.java index 39ea913cf76..4fc2050a117 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnOpen.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnOpen.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.util.Optional; @@ -9,7 +9,7 @@ import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.Edge; +import io.openems.backend.common.metadata.Edge; import io.openems.common.exceptions.OpenemsException; import io.openems.common.utils.JsonUtils; diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnRequest.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnRequest.java similarity index 95% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnRequest.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnRequest.java index 49499eabb49..f6a5251f0e5 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/OnRequest.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/OnRequest.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.util.concurrent.CompletableFuture; diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/SystemLogHandler.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/SystemLogHandler.java similarity index 97% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/SystemLogHandler.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/SystemLogHandler.java index 9d9a8831ac3..166a6e3127d 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/SystemLogHandler.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/SystemLogHandler.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.util.Collection; import java.util.Optional; @@ -11,7 +11,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; -import io.openems.backend.metadata.api.Edge; +import io.openems.backend.common.metadata.Edge; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.DeprecatedJsonrpcNotification; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; @@ -106,7 +106,7 @@ public void handleSystemLogNotification(String edgeId, User user, SystemLogNotif for (UUID token : tokens) { try { this.parent.uiWebsocket.send(token, new EdgeRpcNotification(edgeId, notification)); - } catch (OpenemsNamedException e) { + } catch (OpenemsNamedException | NullPointerException e) { this.log.warn("Unable to handle SystemLogNotification from [" + edgeId + "]: " + e.getMessage()); this.unsubscribe(edgeId, user, token); } diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/WebsocketServer.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/WebsocketServer.java similarity index 97% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/WebsocketServer.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/WebsocketServer.java index 8ae13e2ef5b..3c8ff6ccfef 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/WebsocketServer.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/WebsocketServer.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.time.Instant; import java.time.ZoneId; @@ -37,8 +37,8 @@ public class WebsocketServer extends AbstractWebsocketServer { private final OnError onError; private final OnClose onClose; - public WebsocketServer(EdgeWebsocketImpl parent, String name, int port) { - super(name, port); + public WebsocketServer(EdgeWebsocketImpl parent, String name, int port, int poolSize, boolean debugMode) { + super(name, port, poolSize, debugMode); this.parent = parent; this.onOpen = new OnOpen(parent); this.onRequest = new OnRequest(parent); diff --git a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/WsData.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/WsData.java similarity index 95% rename from io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/WsData.java rename to io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/WsData.java index ffb5b011d49..e8a2a9b91f5 100644 --- a/io.openems.backend.edgewebsocket.impl/src/io/openems/backend/edgewebsocket/impl/WsData.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/WsData.java @@ -1,4 +1,4 @@ -package io.openems.backend.edgewebsocket.impl; +package io.openems.backend.edgewebsocket; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -6,8 +6,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Metadata; +import io.openems.backend.common.metadata.Edge; +import io.openems.backend.common.metadata.Metadata; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.JsonrpcMessage; import io.openems.common.utils.StringUtils; diff --git a/io.openems.backend.edgewebsocket.api/test/.gitignore b/io.openems.backend.edgewebsocket/test/.gitignore similarity index 100% rename from io.openems.backend.edgewebsocket.api/test/.gitignore rename to io.openems.backend.edgewebsocket/test/.gitignore diff --git a/io.openems.backend.metadata.api/.classpath b/io.openems.backend.metadata.api/.classpath deleted file mode 100644 index 7a6fc254361..00000000000 --- a/io.openems.backend.metadata.api/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/io.openems.backend.metadata.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.api/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 74c18943405..00000000000 --- a/io.openems.backend.metadata.api/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/metadata/api/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.metadata.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.metadata.api/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a21537071b..00000000000 --- a/io.openems.backend.metadata.api/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.metadata.api/bnd.bnd b/io.openems.backend.metadata.api/bnd.bnd deleted file mode 100644 index 2177156e215..00000000000 --- a/io.openems.backend.metadata.api/bnd.bnd +++ /dev/null @@ -1,12 +0,0 @@ -Bundle-Name: OpenEMS Backend Metadata Api -Bundle-Vendor: FENECON GmbH -Bundle-License: https://opensource.org/licenses/EPL-2.0 -Bundle-Version: 1.0.0.${tstamp} - --buildpath: \ - ${buildpath},\ - io.openems.backend.edgewebsocket.api,\ - io.openems.common - --testpath: \ - ${testpath} diff --git a/io.openems.backend.metadata.api/test/.gitignore b/io.openems.backend.metadata.api/test/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/io.openems.backend.metadata.dummy/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.metadata.dummy/.settings/org.eclipse.core.resources.prefs index fdc31ea90b5..f2d69683089 100644 --- a/io.openems.backend.metadata.dummy/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.backend.metadata.dummy/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,3 @@ eclipse.preferences.version=1 -encoding//src/io/openems/backend/metadata/dummy/Dummy.java=UTF-8 +encoding//src/io/openems/backend/metadata/dummy/DummyMetadata.java=UTF-8 encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.metadata.dummy/bnd.bnd b/io.openems.backend.metadata.dummy/bnd.bnd index 8da367547b7..fe3224c3aba 100644 --- a/io.openems.backend.metadata.dummy/bnd.bnd +++ b/io.openems.backend.metadata.dummy/bnd.bnd @@ -6,8 +6,6 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.edgewebsocket.api,\ - io.openems.backend.metadata.api,\ io.openems.common -testpath: \ diff --git a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/Dummy.java b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/DummyMetadata.java similarity index 80% rename from io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/Dummy.java rename to io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/DummyMetadata.java index 4b43bb88f7c..ff5ae1a77e2 100644 --- a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/Dummy.java +++ b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/DummyMetadata.java @@ -17,11 +17,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.common.component.AbstractOpenemsBackendComponent; -import io.openems.backend.metadata.api.BackendUser; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Edge.State; -import io.openems.backend.metadata.api.Metadata; +import io.openems.backend.common.metadata.AbstractMetadata; +import io.openems.backend.common.metadata.BackendUser; +import io.openems.backend.common.metadata.Edge; +import io.openems.backend.common.metadata.Edge.State; +import io.openems.backend.common.metadata.Metadata; +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.session.Role; @@ -30,12 +31,15 @@ import io.openems.common.utils.StringUtils; @Designate(ocd = Config.class, factory = false) -@Component(name = "Metadata.Dummy", configurationPolicy = ConfigurationPolicy.REQUIRE) -public class Dummy extends AbstractOpenemsBackendComponent implements Metadata { +@Component(// + name = "Metadata.Dummy", // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +public class DummyMetadata extends AbstractMetadata implements Metadata { private static final Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); - private final Logger log = LoggerFactory.getLogger(Dummy.class); + private final Logger log = LoggerFactory.getLogger(DummyMetadata.class); private final AtomicInteger nextUserId = new AtomicInteger(-1); private final AtomicInteger nextEdgeId = new AtomicInteger(-1); @@ -43,8 +47,9 @@ public class Dummy extends AbstractOpenemsBackendComponent implements Metadata { private final Map users = new HashMap<>(); private final Map edges = new HashMap<>(); - public Dummy() { + public DummyMetadata() { super("Metadata.Dummy"); + this.setInitialized(); } @Activate @@ -88,7 +93,7 @@ public Optional getEdgeIdForApikey(String apikey) { return Optional.ofNullable(edgeOpt.get().getId()); } // not found. Is apikey a valid Edge-ID? - Optional idOpt = Dummy.parseNumberFromName(apikey); + Optional idOpt = DummyMetadata.parseNumberFromName(apikey); int id; String edgeId; if (idOpt.isPresent()) { @@ -99,17 +104,12 @@ public Optional getEdgeIdForApikey(String apikey) { id = this.nextEdgeId.incrementAndGet(); edgeId = "edge" + id; } - MyEdge edge = new MyEdge(edgeId, apikey, "OpenEMS Edge #" + id, State.ACTIVE, "", "", new EdgeConfig()); + MyEdge edge = new MyEdge(edgeId, apikey, "OpenEMS Edge #" + id, State.ACTIVE, "", "", Level.OK, + new EdgeConfig()); edge.onSetConfig(config -> { this.logInfo(this.log, "Edge [" + edgeId + "]. Update config: " + StringUtils.toShortString(EdgeConfigDiff.diff(config, edge.getConfig()).getAsHtml(), 100)); }); - edge.onSetComponentState(activeStateChannels -> { - String states = Metadata.activeStateChannelsToString(activeStateChannels); - if (!states.isEmpty()) { - this.logInfo(this.log, "Edge [" + edgeId + "]. Set State: " + states); - } - }); this.edges.put(edgeId, edge); return Optional.ofNullable(edgeId); @@ -143,9 +143,4 @@ public static Optional parseNumberFromName(String name) { } return Optional.empty(); } - - @Override - public boolean isInitialized() { - return true; - } } diff --git a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MyEdge.java b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MyEdge.java index 2ac1ba0b514..bbf310e2e04 100644 --- a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MyEdge.java +++ b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MyEdge.java @@ -1,6 +1,7 @@ package io.openems.backend.metadata.dummy; -import io.openems.backend.metadata.api.Edge; +import io.openems.backend.common.metadata.Edge; +import io.openems.common.channel.Level; import io.openems.common.types.EdgeConfig; public class MyEdge extends Edge { @@ -8,8 +9,8 @@ public class MyEdge extends Edge { private final String apikey; public MyEdge(String id, String apikey, String comment, State state, String version, String producttype, - EdgeConfig config) { - super(id, comment, state, version, producttype, config); + Level sumState, EdgeConfig config) { + super(id, comment, state, version, producttype, sumState, config); this.apikey = apikey; } diff --git a/io.openems.backend.metadata.file/bnd.bnd b/io.openems.backend.metadata.file/bnd.bnd index f1ed50543b5..a47d266c8df 100644 --- a/io.openems.backend.metadata.file/bnd.bnd +++ b/io.openems.backend.metadata.file/bnd.bnd @@ -6,8 +6,6 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.edgewebsocket.api,\ - io.openems.backend.metadata.api,\ io.openems.common -testpath: \ diff --git a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/File.java b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/FileMetadata.java similarity index 85% rename from io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/File.java rename to io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/FileMetadata.java index 7c7723c4f17..5f58f63d161 100644 --- a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/File.java +++ b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/FileMetadata.java @@ -12,7 +12,6 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -25,11 +24,12 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import io.openems.backend.common.component.AbstractOpenemsBackendComponent; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Edge.State; -import io.openems.backend.metadata.api.Metadata; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.AbstractMetadata; +import io.openems.backend.common.metadata.BackendUser; +import io.openems.backend.common.metadata.Edge; +import io.openems.backend.common.metadata.Edge.State; +import io.openems.backend.common.metadata.Metadata; +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.session.Role; @@ -56,18 +56,20 @@ * user, which has 'ADMIN'-permissions on all given Edges. */ @Designate(ocd = Config.class, factory = false) -@Component(name = "Metadata.File", configurationPolicy = ConfigurationPolicy.REQUIRE) -public class File extends AbstractOpenemsBackendComponent implements Metadata { +@Component(// + name = "Metadata.File", // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +public class FileMetadata extends AbstractMetadata implements Metadata { - private final Logger log = LoggerFactory.getLogger(File.class); + private final Logger log = LoggerFactory.getLogger(FileMetadata.class); private final BackendUser user = new BackendUser("admin", "Administrator"); private final Map edges = new HashMap<>(); - private final AtomicBoolean isInitialized = new AtomicBoolean(false); private String path = ""; - public File() { + public FileMetadata() { super("Metadata.File"); } @@ -162,6 +164,7 @@ private synchronized void refreshData() { State.ACTIVE, // State "", // Version "", // Product-Type + Level.OK, // Sum-State new EdgeConfig() // Config )); } @@ -177,11 +180,7 @@ private synchronized void refreshData() { this.user.addEdgeRole(edge.getId(), Role.ADMIN); } } - this.isInitialized.set(true); + this.setInitialized(); } - @Override - public boolean isInitialized() { - return this.isInitialized.get(); - } } diff --git a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MyEdge.java b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MyEdge.java index c3f653d2a7e..eb94aad6dfe 100644 --- a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MyEdge.java +++ b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MyEdge.java @@ -1,6 +1,7 @@ package io.openems.backend.metadata.file; -import io.openems.backend.metadata.api.Edge; +import io.openems.backend.common.metadata.Edge; +import io.openems.common.channel.Level; import io.openems.common.types.EdgeConfig; public class MyEdge extends Edge { @@ -8,8 +9,8 @@ public class MyEdge extends Edge { private final String apikey; public MyEdge(String id, String apikey, String comment, State state, String version, String producttype, - EdgeConfig config) { - super(id, comment, state, version, producttype, config); + Level sumState, EdgeConfig config) { + super(id, comment, state, version, producttype, sumState, config); this.apikey = apikey; } diff --git a/io.openems.backend.metadata.odoo/bnd.bnd b/io.openems.backend.metadata.odoo/bnd.bnd index 64203ecb9e7..3819735cee4 100644 --- a/io.openems.backend.metadata.odoo/bnd.bnd +++ b/io.openems.backend.metadata.odoo/bnd.bnd @@ -7,8 +7,6 @@ Bundle-Version: 1.0.0.${tstamp} ${buildpath},\ com.zaxxer.HikariCP,\ io.openems.backend.common,\ - io.openems.backend.edgewebsocket.api,\ - io.openems.backend.metadata.api,\ io.openems.common,\ org.apache.servicemix.bundles.xmlrpc-client,\ org.postgresql.jdbc diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java index 5cb9a6ca64b..04efee92e20 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/EdgeCache.java @@ -10,8 +10,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Edge.State; +import io.openems.backend.common.metadata.Edge; +import io.openems.backend.common.metadata.Edge.State; import io.openems.backend.metadata.odoo.Field.EdgeDevice; import io.openems.backend.metadata.odoo.odoo.FieldValue; import io.openems.backend.metadata.odoo.postgres.PgUtils; @@ -19,7 +19,9 @@ import io.openems.backend.metadata.odoo.postgres.task.InsertEdgeConfigUpdate; import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeConfig; import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeProducttype; -import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeVersion; +import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeStateActive; +import io.openems.backend.metadata.odoo.postgres.task.UpdateSumState; +import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.EdgeConfig; @@ -30,7 +32,7 @@ public class EdgeCache { private final Logger log = LoggerFactory.getLogger(EdgeCache.class); - private final MetadataOdoo parent; + private final OdooMetadata parent; /** * Map Edge-ID (String) to Edge. @@ -47,7 +49,7 @@ public class EdgeCache { */ private Map apikeyToEdgeId = new HashMap<>(); - public EdgeCache(MetadataOdoo parent) { + public EdgeCache(OdooMetadata parent) { this.parent = parent; } @@ -97,11 +99,13 @@ public synchronized MyEdge addOrUpate(ResultSet rs) throws SQLException, Openems String comment = PgUtils.getAsStringOrElse(rs, EdgeDevice.COMMENT, ""); String version = PgUtils.getAsStringOrElse(rs, EdgeDevice.OPENEMS_VERSION, ""); String productType = PgUtils.getAsStringOrElse(rs, EdgeDevice.PRODUCT_TYPE, ""); + int sumStateInt = PgUtils.getAsIntegerOrElse(rs, EdgeDevice.OPENEMS_SUM_STATE, -1); + Level sumState = Level.fromValue(sumStateInt).orElse(null); MyEdge edge = this.edgeIdToEdge.get(edgeId); if (edge == null) { // This is new -> create instance of Edge and register listeners - edge = new MyEdge(odooId, edgeId, apikey, comment, state, version, productType, config); + edge = new MyEdge(odooId, edgeId, apikey, comment, state, version, productType, sumState, config); this.addListeners(edge); this.edgeIdToEdge.put(edgeId, edge); this.odooIdToEdgeId.put(odooId, edgeId); @@ -112,6 +116,7 @@ public synchronized MyEdge addOrUpate(ResultSet rs) throws SQLException, Openems edge.setState(state); edge.setVersion(SemanticVersion.fromStringOrZero(version), false); edge.setProducttype(productType); + edge.setSumState(sumState, false); edge.setConfig(config, false); } @@ -181,7 +186,9 @@ private void addListeners(MyEdge edge) { this.parent.logInfo(this.log, "Mark Edge [" + edge.getId() + "] as ACTIVE. It was [" + edge.getState().name() + "]"); edge.setState(State.ACTIVE); - this.parent.odooHandler.writeEdge(edge, new FieldValue(Field.EdgeDevice.STATE, "active")); + this.parent.getPostgresHandler().getQueueWriteWorker(); + QueueWriteWorker queueWriteWorker = this.parent.getPostgresHandler().getQueueWriteWorker(); + queueWriteWorker.addTask(new UpdateEdgeStateActive(edge.getOdooId())); } } else { // Set OpenEMS Is Connected in Odoo/Postgres @@ -210,15 +217,16 @@ private void addListeners(MyEdge edge) { this.parent.getPostgresHandler().getPeriodicWriteWorker().onLastUpdate(edge); }); edge.onSetVersion(version -> { - // Set Version in Odoo/Postgres + // Set Version in Odoo this.parent.logInfo(this.log, "Edge [" + edge.getId() + "]: Update OpenEMS Edge version to [" + version + "]. It was [" + edge.getVersion() + "]"); - - this.parent.getPostgresHandler().getQueueWriteWorker() - .addTask(new UpdateEdgeVersion(edge.getOdooId(), version)); + this.parent.odooHandler.writeEdge(edge, + new FieldValue(Field.EdgeDevice.OPENEMS_VERSION, version.toString())); }); - edge.onSetComponentState(activeStateChannels -> { - this.parent.postgresHandler.updateDeviceStates(edge, activeStateChannels); + edge.onSetSumState(sumState -> { + // Set Sum-State in Odoo/Postgres + this.parent.getPostgresHandler().getQueueWriteWorker() + .addTask(new UpdateSumState(edge.getOdooId(), sumState)); }); edge.onSetProducttype(producttype -> { // Set Producttype in Odoo/Postgres diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java index 7d18d6ac6bf..f6eea9289f1 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/Field.java @@ -40,8 +40,7 @@ public enum EdgeDevice implements Field { OPENEMS_CONFIG_COMPONENTS("openems_config_components", false), // LAST_MESSAGE("lastmessage", false), // LAST_UPDATE("lastupdate", false), // - OPENEMS_SUM_STATE("openems_sum_state_level", false), // - OPENEMS_SUM_STATE_TEXT("openems_sum_state_text", false), // + OPENEMS_SUM_STATE("openems_sum_state_level", true), // OPENEMS_IS_CONNECTED("openems_is_connected", false); public static final String ODOO_MODEL = "edge.device"; diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java index c1376390fd3..3f32bdad306 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyEdge.java @@ -1,6 +1,7 @@ package io.openems.backend.metadata.odoo; -import io.openems.backend.metadata.api.Edge; +import io.openems.backend.common.metadata.Edge; +import io.openems.common.channel.Level; import io.openems.common.types.EdgeConfig; public class MyEdge extends Edge { @@ -9,8 +10,8 @@ public class MyEdge extends Edge { private final String apikey; public MyEdge(int odooId, String edgeId, String apikey, String comment, State state, String version, - String producttype, EdgeConfig config) { - super(edgeId, comment, state, version, producttype, config); + String producttype, Level sumState, EdgeConfig config) { + super(edgeId, comment, state, version, producttype, sumState, config); this.apikey = apikey; this.odooId = odooId; } diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyUser.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyUser.java index 2d804098371..82aefe207de 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyUser.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MyUser.java @@ -1,6 +1,6 @@ package io.openems.backend.metadata.odoo; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; public class MyUser extends BackendUser { diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MetadataOdoo.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooMetadata.java similarity index 84% rename from io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MetadataOdoo.java rename to io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooMetadata.java index 4b1715fa727..5ee2ad19424 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/MetadataOdoo.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/OdooMetadata.java @@ -4,7 +4,6 @@ import java.util.Collection; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -14,10 +13,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.common.component.AbstractOpenemsBackendComponent; -import io.openems.backend.metadata.api.BackendUser; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Metadata; +import io.openems.backend.common.metadata.AbstractMetadata; +import io.openems.backend.common.metadata.BackendUser; +import io.openems.backend.common.metadata.Edge; +import io.openems.backend.common.metadata.Metadata; import io.openems.backend.metadata.odoo.odoo.OdooHandler; import io.openems.backend.metadata.odoo.odoo.jsonrpc.AuthenticateWithSessionIdResponse; import io.openems.backend.metadata.odoo.postgres.PostgresHandler; @@ -26,23 +25,24 @@ import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; @Designate(ocd = Config.class, factory = false) -@Component(name = "Metadata.Odoo", configurationPolicy = ConfigurationPolicy.REQUIRE) -public class MetadataOdoo extends AbstractOpenemsBackendComponent implements Metadata { +@Component(// + name = "Metadata.Odoo", // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +public class OdooMetadata extends AbstractMetadata implements Metadata { - private final Logger log = LoggerFactory.getLogger(MetadataOdoo.class); + private final Logger log = LoggerFactory.getLogger(OdooMetadata.class); private final EdgeCache edgeCache; protected OdooHandler odooHandler = null; protected PostgresHandler postgresHandler = null; - private final AtomicBoolean isInitialized = new AtomicBoolean(false); - /** * Maps User-ID to User. */ private ConcurrentHashMap users = new ConcurrentHashMap<>(); - public MetadataOdoo() { + public OdooMetadata() { super("Metadata.Odoo"); this.edgeCache = new EdgeCache(this); @@ -59,7 +59,7 @@ void activate(Config config) throws SQLException { this.odooHandler = new OdooHandler(this, config); this.postgresHandler = new PostgresHandler(this, edgeCache, config, () -> { - this.isInitialized.set(true); + this.setInitialized(); }); } @@ -71,11 +71,6 @@ void deactivate() { } } - @Override - public boolean isInitialized() { - return this.isInitialized.get(); - } - @Override public BackendUser authenticate(String username, String password) throws OpenemsNamedException { String sessionId = this.odooHandler.authenticate(username, password); @@ -146,5 +141,4 @@ public void logWarn(Logger log, String message) { public void logError(Logger log, String message) { super.logError(log, message); } - } diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java index da16ad23781..30fc3300a1d 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java @@ -11,7 +11,7 @@ import io.openems.backend.metadata.odoo.Config; import io.openems.backend.metadata.odoo.Field; -import io.openems.backend.metadata.odoo.MetadataOdoo; +import io.openems.backend.metadata.odoo.OdooMetadata; import io.openems.backend.metadata.odoo.MyEdge; import io.openems.backend.metadata.odoo.odoo.OdooUtils.JsonrpcResponseSuccessAndHeaders; import io.openems.backend.metadata.odoo.odoo.jsonrpc.AuthenticateWithUsernameAndPasswordRequest; @@ -22,12 +22,12 @@ public class OdooHandler { - protected final MetadataOdoo parent; + protected final OdooMetadata parent; private final Logger log = LoggerFactory.getLogger(OdooHandler.class); private final Credentials credentials; - public OdooHandler(MetadataOdoo parent, Config config) { + public OdooHandler(OdooMetadata parent, Config config) { this.parent = parent; this.credentials = Credentials.fromConfig(config); } diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/jsonrpc/OdooCallRequest.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/jsonrpc/OdooCallRequest.java index 08a90e263fa..7ac1d8561d9 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/jsonrpc/OdooCallRequest.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/jsonrpc/OdooCallRequest.java @@ -1,7 +1,5 @@ package io.openems.backend.metadata.odoo.odoo.jsonrpc; -import java.util.UUID; - import io.openems.common.jsonrpc.base.JsonrpcRequest; /** @@ -20,12 +18,8 @@ public abstract class OdooCallRequest extends JsonrpcRequest { public static final String METHOD = "call"; - public OdooCallRequest(UUID id) { - super(id, METHOD); - } - - public OdooCallRequest() { - this(UUID.randomUUID()); + protected OdooCallRequest() { + super(METHOD); } } diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PeriodicWriteWorker.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PeriodicWriteWorker.java index 2ea77db4333..5e6e9f7be7c 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PeriodicWriteWorker.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PeriodicWriteWorker.java @@ -21,7 +21,6 @@ import io.openems.backend.metadata.odoo.Field; import io.openems.backend.metadata.odoo.Field.EdgeDevice; import io.openems.backend.metadata.odoo.MyEdge; -import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeStatesSum; /** * This worker combines writes to lastMessage and lastUpdate fields, to avoid @@ -34,7 +33,7 @@ public class PeriodicWriteWorker { */ private static final boolean DEBUG_MODE = true; - private static final int UPDATE_INTERVAL_IN_SECONDS = 60; + private static final int UPDATE_INTERVAL_IN_SECONDS = 120; private final Logger log = LoggerFactory.getLogger(PeriodicWriteWorker.class); private final PostgresHandler parent; @@ -92,7 +91,6 @@ public synchronized void stop() { this.writeLastUpdate(dataSource); this.writeIsOnline(dataSource); this.writeIsOffline(dataSource); - this.updateEdgeStatesSum(dataSource); } catch (SQLException e) { this.log.error("Unable to execute WriteWorker task: " + e.getMessage()); @@ -117,21 +115,6 @@ public void triggerUpdateEdgeStatesSum(MyEdge edge) { } } - private void updateEdgeStatesSum(HikariDataSource dataSource) throws SQLException { - Set edgeIds; - synchronized (this.updateEdgeStatesSum) { - edgeIds = new HashSet<>(this.updateEdgeStatesSum); - } - - try { - new UpdateEdgeStatesSum(edgeIds).execute(dataSource); - } catch (SQLException e) { - this.parent.logWarn(this.log, - "Unable to execute Task. " + this.task.getClass().getSimpleName() + ": " + e.getMessage()); - e.printStackTrace(); - } - } - private void writeIsOffline(HikariDataSource dataSource) throws SQLException { synchronized (this.isOfflineOdooIds) { if (!this.isOfflineOdooIds.isEmpty()) { diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PostgresHandler.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PostgresHandler.java index 707314b4775..8074f773ae7 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PostgresHandler.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/PostgresHandler.java @@ -18,7 +18,7 @@ import io.openems.backend.metadata.odoo.Config; import io.openems.backend.metadata.odoo.EdgeCache; -import io.openems.backend.metadata.odoo.MetadataOdoo; +import io.openems.backend.metadata.odoo.OdooMetadata; import io.openems.backend.metadata.odoo.MyEdge; import io.openems.backend.metadata.odoo.postgres.task.InsertOrUpdateDeviceStates; import io.openems.common.channel.Level; @@ -31,13 +31,13 @@ public class PostgresHandler { protected final EdgeCache edgeCache; - private final MetadataOdoo parent; + private final OdooMetadata parent; private final HikariDataSource dataSource; private final InitializeEdgesWorker initializeEdgesWorker; private final PeriodicWriteWorker periodicWriteWorker; private final QueueWriteWorker queueWriteWorker; - public PostgresHandler(MetadataOdoo parent, EdgeCache edgeCache, Config config, Runnable onInitialized) + public PostgresHandler(OdooMetadata parent, EdgeCache edgeCache, Config config, Runnable onInitialized) throws SQLException { this.parent = parent; this.edgeCache = edgeCache; diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/QueueWriteWorker.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/QueueWriteWorker.java index 948744ecd7a..cc57b04d221 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/QueueWriteWorker.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/QueueWriteWorker.java @@ -2,8 +2,8 @@ import java.sql.SQLException; import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -18,27 +18,25 @@ import io.openems.backend.metadata.odoo.postgres.task.InsertOrUpdateDeviceStates; import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeConfig; import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeProducttype; -import io.openems.backend.metadata.odoo.postgres.task.UpdateEdgeVersion; /** * This worker writes all Statements in a queue. */ public class QueueWriteWorker { - private static final int NUMBER_OF_THREADS = 5; - /** * DEBUG_MODE activates printing of reqular statistics about queued tasks. */ - private static final boolean DEBUG_MODE = true; + private final static boolean DEBUG_MODE = false; private final Logger log = LoggerFactory.getLogger(QueueWriteWorker.class); private final PostgresHandler parent; private final HikariDataSource dataSource; - // Executor for subscriptions task. - private final ThreadPoolExecutor executor = new ThreadPoolExecutor(NUMBER_OF_THREADS, NUMBER_OF_THREADS, 0L, - TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); + // Executor for subscriptions task. Like a CachedThreadPool, but properly typed + // for DEBUG_MODE. + private final ThreadPoolExecutor executor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, + new SynchronousQueue()); private final ScheduledExecutorService debugLogExecutor; @@ -115,8 +113,6 @@ private void initializeDebugLog() { int countUpdateEdgeConfigDown = this.countUpdateEdgeConfigDown.get(); int countUpdateEdgeProducttypeUp = this.countUpdateEdgeProducttypeUp.get(); int countUpdateEdgeProducttypeDown = this.countUpdateEdgeProducttypeDown.get(); - int countUpdateEdgeVersionUp = this.countUpdateEdgeVersionUp.get(); - int countUpdateEdgeVersionDown = this.countUpdateEdgeVersionDown.get(); this.parent.logInfo(this.log, "QueueWriteWorker. " // + "Total tasks [" + totalTasks + "|" + completedTasks + "|" + (totalTasks - completedTasks) + "] " // @@ -130,8 +126,6 @@ private void initializeDebugLog() { + (countUpdateEdgeConfigUp - countUpdateEdgeConfigDown) + "] " // + "UpdateEdgeProducttype [" + countUpdateEdgeProducttypeUp + "|" + countUpdateEdgeProducttypeDown + "|" + (countUpdateEdgeProducttypeUp - countUpdateEdgeProducttypeDown) + "] " // - + "UpdateEdgeVersion [" + countUpdateEdgeVersionUp + "|" + countUpdateEdgeVersionDown + "|" - + (countUpdateEdgeVersionUp - countUpdateEdgeVersionDown) + "] " // ); }, 10, 10, TimeUnit.SECONDS); } @@ -144,8 +138,6 @@ private void initializeDebugLog() { private final AtomicInteger countUpdateEdgeConfigDown = new AtomicInteger(0); private final AtomicInteger countUpdateEdgeProducttypeUp = new AtomicInteger(0); private final AtomicInteger countUpdateEdgeProducttypeDown = new AtomicInteger(0); - private final AtomicInteger countUpdateEdgeVersionUp = new AtomicInteger(0); - private final AtomicInteger countUpdateEdgeVersionDown = new AtomicInteger(0); private void count(DatabaseTask task, boolean up) { if (up) { @@ -158,8 +150,6 @@ private void count(DatabaseTask task, boolean up) { counter = this.countUpdateEdgeConfigUp; } else if (task instanceof UpdateEdgeProducttype) { counter = this.countUpdateEdgeProducttypeUp; - } else if (task instanceof UpdateEdgeVersion) { - counter = this.countUpdateEdgeVersionUp; } else { System.out.println("Unknown Task " + task.getClass()); return; @@ -175,8 +165,6 @@ private void count(DatabaseTask task, boolean up) { counter = this.countUpdateEdgeConfigDown; } else if (task instanceof UpdateEdgeProducttype) { counter = this.countUpdateEdgeProducttypeDown; - } else if (task instanceof UpdateEdgeVersion) { - counter = this.countUpdateEdgeVersionDown; } else { System.out.println("Unknown Task " + task.getClass()); return; diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/InsertOrUpdateDeviceStates.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/InsertOrUpdateDeviceStates.java index d10d802035f..b858d103d3c 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/InsertOrUpdateDeviceStates.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/InsertOrUpdateDeviceStates.java @@ -81,4 +81,4 @@ private PreparedStatement psInsertOrUpdateDeviceState(Connection connection) thr public String toString() { return "InsertOrUpdateDeviceState [odooId=" + this.odooId + ", timestamp=" + this.timestamp + "]"; } -} \ No newline at end of file +} diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateEdgeStateActive.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateEdgeStateActive.java new file mode 100644 index 00000000000..ef5626e647e --- /dev/null +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateEdgeStateActive.java @@ -0,0 +1,43 @@ +package io.openems.backend.metadata.odoo.postgres.task; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import io.openems.backend.metadata.odoo.Field.EdgeDevice; + +public class UpdateEdgeStateActive extends DatabaseTask { + + private final int odooId; + + public UpdateEdgeStateActive(int odooId) { + this.odooId = odooId; + } + + @Override + protected void _execute(Connection connection) throws SQLException { + PreparedStatement ps = this.psUpdateEdgeStateActive(connection); + ps.setInt(1, this.odooId); + ps.execute(); + } + + /** + * UPDATE {} SET state = 'active' WHERE id = {};. + * + * @return the PreparedStatement + * @throws SQLException on error + */ + private PreparedStatement psUpdateEdgeStateActive(Connection connection) throws SQLException { + return connection.prepareStatement(// + "UPDATE " + EdgeDevice.ODOO_TABLE // + + " SET" // + + " " + EdgeDevice.STATE.id() + " = 'active'" // + + " WHERE id = ?"); + } + + @Override + public String toString() { + return "UpdateEdgeStateActive [odooId=" + this.odooId + "]"; + } + +} \ No newline at end of file diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateEdgeStatesSum.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateEdgeStatesSum.java deleted file mode 100644 index dd21ddfeab4..00000000000 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateEdgeStatesSum.java +++ /dev/null @@ -1,133 +0,0 @@ -package io.openems.backend.metadata.odoo.postgres.task; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.TreeMultimap; - -import io.openems.backend.metadata.odoo.Field; -import io.openems.backend.metadata.odoo.Field.EdgeDevice; -import io.openems.backend.metadata.odoo.Field.EdgeDeviceStatus; -import io.openems.backend.metadata.odoo.postgres.PgUtils; -import io.openems.common.channel.Level; -import io.openems.common.exceptions.OpenemsException; - -public class UpdateEdgeStatesSum extends DatabaseTask { - - private final Logger log = LoggerFactory.getLogger(UpdateEdgeStatesSum.class); - - private final Set odooIds; - - public UpdateEdgeStatesSum(Set odooIds) { - this.odooIds = odooIds; - } - - @Override - protected void _execute(Connection connection) throws SQLException { - PreparedStatement psQueryNotAcknowledgedDeviceStates = this.psQueryNotAcknowledgedDeviceStates(connection); - PreparedStatement psUpdateEdgeState = this.psUpdateEdgeState(connection); - - for (int odooId : this.odooIds) { - /* - * Query non-acknowledged states - */ - Level highestLevel = Level.OK; - String stateChannels; - psQueryNotAcknowledgedDeviceStates.setInt(1, odooId); - ResultSet rs = psQueryNotAcknowledgedDeviceStates.executeQuery(); - TreeMap> levels = new TreeMap<>( - (l1, l2) -> Integer.compare(l1.getValue(), l2.getValue() * -1)); - while (rs.next()) { - try { - // Parse ResultSet - Level level = Level.valueOf(PgUtils.getAsString(rs, EdgeDeviceStatus.LEVEL).toUpperCase()); - - if (level == Level.OK) { - // ignore OK-Channels; no need to acknowledge them - continue; - } - - String componentId = PgUtils.getAsString(rs, EdgeDeviceStatus.COMPONENT_ID); - String channelName = PgUtils.getAsString(rs, EdgeDeviceStatus.CHANNEL_NAME); - - // Update highest level - if (level.getValue() > highestLevel.getValue()) { - highestLevel = level; - } - - // Add StateChannel to Map - TreeMultimap componentIds = levels.get(level); - if (componentIds == null) { - componentIds = TreeMultimap.create(); - levels.put(level, componentIds); - } - componentIds.put(componentId, channelName); - } catch (OpenemsException e) { - this.log.warn("While updating Edge States: " + e.getMessage()); - } - } - - // Generate State-Channels-String - stateChannels = levels.entrySet().stream().map(levelsEntry -> { - return levelsEntry.getKey().name().toUpperCase() + " " // - + levelsEntry.getValue().asMap().entrySet().stream().map(componentIdsEntry -> { - return componentIdsEntry.getKey() + ": " + String.join(", ", componentIdsEntry.getValue()); - }).collect(Collectors.joining(";")); - }).collect(Collectors.joining(" ")); - /* - * Update Edge openems_sum_state_level and openems_sum_state_text - */ - psUpdateEdgeState.setString(1, highestLevel.name().toLowerCase()); - psUpdateEdgeState.setString(2, stateChannels); - psUpdateEdgeState.setInt(3, odooId); - psUpdateEdgeState.execute(); - } - } - - /** - * SELECT level, component_id, channel_name FROM {} WHERE device_id = {} AND ... - * - * @return the PreparedStatement - * @throws SQLException on error - */ - private PreparedStatement psQueryNotAcknowledgedDeviceStates(Connection connection) throws SQLException { - return connection.prepareStatement(// - "SELECT " + Field.getSqlQueryFields(EdgeDeviceStatus.values()) // - + " FROM " + EdgeDeviceStatus.ODOO_TABLE // - + " WHERE device_id = ?" // - + " AND (" // - + " last_acknowledge IS NULL" - + " OR (acknowledge_days > 0 AND last_appearance > last_acknowledge + interval '1 day' * acknowledge_days)" // - + ")"); - } - - /** - * UPDATE {} SET openems_sum_state_level = {}, openems_sum_state_text = {} WHERE - * id = {};. - * - * @return the PreparedStatement - * @throws SQLException on error - */ - private PreparedStatement psUpdateEdgeState(Connection connection) throws SQLException { - return connection.prepareStatement(// - "UPDATE " + EdgeDevice.ODOO_TABLE // - + " SET" // - + " " + EdgeDevice.OPENEMS_SUM_STATE.id() + " = ?," // - + " " + EdgeDevice.OPENEMS_SUM_STATE_TEXT.id() + " = ?" // - + " WHERE id = ?"); - } - - @Override - public String toString() { - return "UpdateEdgeStatesSum []"; - } - -} \ No newline at end of file diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateSumState.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateSumState.java new file mode 100644 index 00000000000..8b38827a36b --- /dev/null +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/postgres/task/UpdateSumState.java @@ -0,0 +1,53 @@ +package io.openems.backend.metadata.odoo.postgres.task; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import io.openems.backend.metadata.odoo.Field.EdgeDevice; +import io.openems.common.channel.Level; + +public class UpdateSumState extends DatabaseTask { + + private final int odooId; + private final Level sumState; + + public UpdateSumState(int odooId, Level sumState) { + this.odooId = odooId; + this.sumState = sumState; + } + + @Override + protected void _execute(Connection connection) throws SQLException { + PreparedStatement ps = this.psUpdateSumState(connection); + final String sumStateString; + if (this.sumState != null) { + sumStateString = sumState.getName().toLowerCase(); + } else { + sumStateString = ""; + } + ps.setString(1, sumStateString); + ps.setInt(2, this.odooId); + ps.execute(); + } + + /** + * UPDATE {} SET openems_sum_state_level = {} WHERE id = {};. + * + * @return the PreparedStatement + * @throws SQLException on error + */ + private PreparedStatement psUpdateSumState(Connection connection) throws SQLException { + return connection.prepareStatement(// + "UPDATE " + EdgeDevice.ODOO_TABLE // + + " SET" // + + " " + EdgeDevice.OPENEMS_SUM_STATE.id() + " = ?" // + + " WHERE id = ?"); + } + + @Override + public String toString() { + return "UpdateSumState [odooId=" + this.odooId + ", sumState=" + this.sumState + "]"; + } + +} \ No newline at end of file diff --git a/io.openems.backend.timedata.api/.classpath b/io.openems.backend.timedata.api/.classpath deleted file mode 100644 index 7a6fc254361..00000000000 --- a/io.openems.backend.timedata.api/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/io.openems.backend.timedata.api/.gitignore b/io.openems.backend.timedata.api/.gitignore deleted file mode 100644 index 90dde36e4ac..00000000000 --- a/io.openems.backend.timedata.api/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/bin/ -/bin_test/ -/generated/ diff --git a/io.openems.backend.timedata.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.timedata.api/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index d39eadd5891..00000000000 --- a/io.openems.backend.timedata.api/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/timedata/api/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.timedata.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.timedata.api/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a21537071b..00000000000 --- a/io.openems.backend.timedata.api/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.timedata.api/bnd.bnd b/io.openems.backend.timedata.api/bnd.bnd deleted file mode 100644 index 7703378049d..00000000000 --- a/io.openems.backend.timedata.api/bnd.bnd +++ /dev/null @@ -1,12 +0,0 @@ -Bundle-Name: OpenEMS Backend Timedata Api -Bundle-Vendor: FENECON GmbH -Bundle-License: https://opensource.org/licenses/EPL-2.0 -Bundle-Version: 1.0.0.${tstamp} - --buildpath: \ - ${buildpath},\ - io.openems.backend.metadata.api,\ - io.openems.common - --testpath: \ - ${testpath} diff --git a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/EdgeCache.java b/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/EdgeCache.java deleted file mode 100644 index 3222e5677de..00000000000 --- a/io.openems.backend.timedata.api/src/io/openems/backend/timedata/api/EdgeCache.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.openems.backend.timedata.api; - -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -import com.google.gson.JsonElement; - -import io.openems.common.types.ChannelAddress; - -public class EdgeCache { - - private long timestamp = 0l; - private final ConcurrentHashMap channelValueCache = new ConcurrentHashMap<>(); - - public synchronized final Optional getChannelValue(ChannelAddress address) { - return Optional.ofNullable(this.channelValueCache.get(address)); - } - - public synchronized final ConcurrentHashMap getChannelCacheEntries() { - return this.channelValueCache; - } - - /** - * Adds the channel value to the cache - * - * @param channel the Channel-Address - * @param value the Value as a JsonElement - */ - public synchronized void putToChannelCache(ChannelAddress channel, JsonElement value) { - this.channelValueCache.put(channel, value); - } - - public long getTimestamp() { - return timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - public void clear() { - this.channelValueCache.clear(); - } - -} diff --git a/io.openems.backend.timedata.api/test/.gitignore b/io.openems.backend.timedata.api/test/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/io.openems.backend.timedata.dummy/bnd.bnd b/io.openems.backend.timedata.dummy/bnd.bnd index 48947f8f61c..32adbd4a385 100644 --- a/io.openems.backend.timedata.dummy/bnd.bnd +++ b/io.openems.backend.timedata.dummy/bnd.bnd @@ -6,8 +6,6 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.metadata.api,\ - io.openems.backend.timedata.api,\ io.openems.common -testpath: \ diff --git a/io.openems.backend.timedata.dummy/src/io/openems/backend/timedata/dummy/TimedataDummy.java b/io.openems.backend.timedata.dummy/src/io/openems/backend/timedata/dummy/TimedataDummy.java index 10b3b6ffddd..3e0d41e2c0e 100644 --- a/io.openems.backend.timedata.dummy/src/io/openems/backend/timedata/dummy/TimedataDummy.java +++ b/io.openems.backend.timedata.dummy/src/io/openems/backend/timedata/dummy/TimedataDummy.java @@ -3,7 +3,6 @@ import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.SortedMap; @@ -21,10 +20,10 @@ import com.google.gson.JsonElement; import io.openems.backend.common.component.AbstractOpenemsBackendComponent; -import io.openems.backend.timedata.api.EdgeCache; -import io.openems.backend.timedata.api.Timedata; -import io.openems.common.exceptions.OpenemsException; +import io.openems.backend.common.timedata.EdgeCache; +import io.openems.backend.common.timedata.Timedata; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; @Designate(ocd = Config.class, factory = false) @@ -66,42 +65,9 @@ public void write(String edgeId, TreeBasedTable> dataEntry : data.rowMap().entrySet()) { - Long timestamp = dataEntry.getKey(); - Map channels = dataEntry.getValue(); - - // Check if cache is valid (it is not elder than 5 minutes compared to this - // timestamp) - long cacheTimestamp = edgeCache.getTimestamp(); - if (timestamp < cacheTimestamp) { - // incoming data is older than cache -> do not apply cache - } else { - // incoming data is more recent than cache - // update cache timestamp - edgeCache.setTimestamp(timestamp); - - if (timestamp < cacheTimestamp + 5 * 60 * 1000) { - // cache is valid (not elder than 5 minutes) - } else { - // cache is not anymore valid (elder than 5 minutes) - // clear cache - if (cacheTimestamp != 0L) { - this.logInfo(this.log, "Edge [" + edgeId + "]: invalidate cache. This timestamp [" + timestamp - + "]. Cache timestamp [" + cacheTimestamp + "]"); - } - edgeCache.clear(); - } - - // add incoming data to cache (this replaces already existing cache values) - for (Entry channelEntry : channels.entrySet()) { - ChannelAddress channel = channelEntry.getKey(); - JsonElement value = channelEntry.getValue(); - edgeCache.putToChannelCache(channel, value); - } - } - } + // Complement incoming data with data from Cache, because only changed values + // are transmitted + edgeCache.complementDataFromCache(edgeId, data.rowMap()); } @Override diff --git a/io.openems.backend.timedata.influx/bnd.bnd b/io.openems.backend.timedata.influx/bnd.bnd index 38e32f8d731..bb3959655e3 100644 --- a/io.openems.backend.timedata.influx/bnd.bnd +++ b/io.openems.backend.timedata.influx/bnd.bnd @@ -6,8 +6,6 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.metadata.api,\ - io.openems.backend.timedata.api,\ io.openems.common,\ io.openems.shared.influxdb,\ io.openems.wrapper.influxdb-java diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/ChannelFormula.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/ChannelFormula.java index 04a43e487ac..c65be3342fd 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/ChannelFormula.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/ChannelFormula.java @@ -4,7 +4,7 @@ import com.google.gson.JsonPrimitive; -import io.openems.backend.timedata.api.EdgeCache; +import io.openems.backend.common.timedata.EdgeCache; import io.openems.common.types.ChannelAddress; public class ChannelFormula { diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/FieldTypeConflictHandler.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/FieldTypeConflictHandler.java index 435f97e47c2..d227e5e6dcc 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/FieldTypeConflictHandler.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/FieldTypeConflictHandler.java @@ -55,17 +55,27 @@ public synchronized void handleException(FieldTypeConflictException e) { switch (requiredType) { case "string": handler = (builder, jValue) -> { - builder.addField(field, this.getAsFieldTypeString(jValue)); + String value = this.getAsFieldTypeString(jValue); + if (value != null) { + builder.addField(field, value); + } }; break; + case "integer": handler = (builder, jValue) -> { try { - builder.addField(field, this.getAsFieldTypeNumber(jValue)); + Number value = this.getAsFieldTypeNumber(jValue); + if (value != null) { + builder.addField(field, value); + } } catch (NumberFormatException e1) { try { // Failed -> try conversion to float and then to int - builder.addField(field, Math.round(this.getAsFieldTypeFloat(jValue))); + Float value = this.getAsFieldTypeFloat(jValue); + if (value != null) { + builder.addField(field, Math.round(value)); + } } catch (NumberFormatException e2) { this.parent.logWarn(this.log, "Unable to convert field [" + field + "] value [" + jValue + "] to integer: " + e2.getMessage()); @@ -73,13 +83,16 @@ public synchronized void handleException(FieldTypeConflictException e) { } }; break; + case "float": handler = (builder, jValue) -> { - String value = jValue.toString().replace("\"", ""); try { - builder.addField(field, this.getAsFieldTypeFloat(jValue)); + Float value = this.getAsFieldTypeFloat(jValue); + if (value != null) { + builder.addField(field, value); + } } catch (NumberFormatException e1) { - this.parent.logInfo(this.log, "Unable to convert field [" + field + "] value [" + value + this.parent.logInfo(this.log, "Unable to convert field [" + field + "] value [" + jValue + "] to float: " + e1.getMessage()); } }; @@ -101,9 +114,12 @@ public synchronized void handleException(FieldTypeConflictException e) { * Convert JsonElement to String * * @param jValue the value - * @return the value as String + * @return the value as String; null if value represents null */ private String getAsFieldTypeString(JsonElement jValue) { + if (jValue.isJsonNull()) { + return null; + } return jValue.toString().replace("\"", ""); } @@ -111,11 +127,17 @@ private String getAsFieldTypeString(JsonElement jValue) { * Convert JsonElement to Number * * @param jValue the value - * @return the value as Number + * @return the value as Number; null if value represents null * @throws NumberFormatException on error */ - private long getAsFieldTypeNumber(JsonElement jValue) throws NumberFormatException { + private Number getAsFieldTypeNumber(JsonElement jValue) throws NumberFormatException { + if (jValue.isJsonNull()) { + return null; + } String value = jValue.toString().replace("\"", ""); + if (value.isEmpty()) { + return null; + } try { return Long.parseLong(value); } catch (NumberFormatException e1) { @@ -133,11 +155,17 @@ private long getAsFieldTypeNumber(JsonElement jValue) throws NumberFormatExcepti * Convert JsonElement to Float * * @param jValue the value - * @return the value as Float + * @return the value as Float; null if value represents null * @throws NumberFormatException on error */ - private float getAsFieldTypeFloat(JsonElement jValue) throws NumberFormatException { + private Float getAsFieldTypeFloat(JsonElement jValue) throws NumberFormatException { + if (jValue.isJsonNull()) { + return null; + } String value = jValue.toString().replace("\"", ""); + if (value.isEmpty()) { + return null; + } return Float.parseFloat(value); } diff --git a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java index fd90534687f..a00b25b0572 100644 --- a/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java +++ b/io.openems.backend.timedata.influx/src/io/openems/backend/timedata/influx/Influx.java @@ -31,10 +31,10 @@ import com.google.gson.JsonPrimitive; import io.openems.backend.common.component.AbstractOpenemsBackendComponent; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.Metadata; -import io.openems.backend.timedata.api.EdgeCache; -import io.openems.backend.timedata.api.Timedata; +import io.openems.backend.common.metadata.Edge; +import io.openems.backend.common.metadata.Metadata; +import io.openems.backend.common.timedata.EdgeCache; +import io.openems.backend.common.timedata.Timedata; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; @@ -45,7 +45,10 @@ import io.openems.shared.influxdb.InfluxConstants; @Designate(ocd = Config.class, factory = false) -@Component(name = "Timedata.InfluxDB", configurationPolicy = ConfigurationPolicy.REQUIRE) +@Component(// + name = "Timedata.InfluxDB", // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) public class Influx extends AbstractOpenemsBackendComponent implements Timedata { private static final Pattern NAME_NUMBER_PATTERN = Pattern.compile("[^0-9]+([0-9]+)$"); @@ -111,50 +114,9 @@ public void write(String edgeId, TreeBasedTable> entry : data.rowMap().entrySet()) { - Long timestamp = entry.getKey(); - - // Check if cache is valid (it is not elder than 5 minutes compared to this - // timestamp) - long cacheTimestamp = edgeCache.getTimestamp(); - if (timestamp < cacheTimestamp) { - // incoming data is older than cache -> do not apply cache - - } else { - // incoming data is more recent than cache - if (timestamp < cacheTimestamp + 5 * 60 * 1000) { - // cache is valid (not elder than 5 minutes) - for (Entry cacheEntry : edgeCache.getChannelCacheEntries() - .entrySet()) { - ChannelAddress channel = cacheEntry.getKey(); - // check if there is a current value for this timestamp + channel - JsonElement existingValue = data.get(timestamp, channel); - if (existingValue == null) { - // if not -> add cache data to write data - data.put(timestamp, channel, cacheEntry.getValue()); - } - } - } else { - // cache is not anymore valid (elder than 5 minutes) - if (cacheTimestamp != 0L) { - this.logInfo(this.log, "Edge [" + edgeId + "]: invalidate cache for influxId [" + influxEdgeId - + "]. This timestamp [" + timestamp + "]. Cache timestamp [" + cacheTimestamp + "]"); - } - // clear cache - edgeCache.clear(); - } - - // update cache - edgeCache.setTimestamp(timestamp); - for (Entry channelEntry : entry.getValue().entrySet()) { - edgeCache.putToChannelCache(channelEntry.getKey(), channelEntry.getValue()); - } - } - } + // Complement incoming data with data from Cache, because only changed values + // are transmitted + edgeCache.complementDataFromCache(edgeId, data.rowMap()); // Write data to default location this.writeData(influxEdgeId, data); diff --git a/io.openems.backend.uiwebsocket.api/.classpath b/io.openems.backend.uiwebsocket.api/.classpath deleted file mode 100644 index 7a6fc254361..00000000000 --- a/io.openems.backend.uiwebsocket.api/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/io.openems.backend.uiwebsocket.api/.gitignore b/io.openems.backend.uiwebsocket.api/.gitignore deleted file mode 100644 index 90dde36e4ac..00000000000 --- a/io.openems.backend.uiwebsocket.api/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/bin/ -/bin_test/ -/generated/ diff --git a/io.openems.backend.uiwebsocket.api/.project b/io.openems.backend.uiwebsocket.api/.project deleted file mode 100644 index 6c0d04fc084..00000000000 --- a/io.openems.backend.uiwebsocket.api/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - io.openems.backend.uiwebsocket.api - - - - - - org.eclipse.jdt.core.javabuilder - - - - - bndtools.core.bndbuilder - - - - - - org.eclipse.jdt.core.javanature - bndtools.core.bndnature - - diff --git a/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 4c75c3d62f5..00000000000 --- a/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/io/openems/backend/uiwebsocket/api/package-info.java=UTF-8 -encoding//test/.gitignore=UTF-8 -encoding/bnd.bnd=UTF-8 diff --git a/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a21537071b..00000000000 --- a/io.openems.backend.uiwebsocket.api/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.uiwebsocket.api/bnd.bnd b/io.openems.backend.uiwebsocket.api/bnd.bnd deleted file mode 100644 index fd92ed80ff7..00000000000 --- a/io.openems.backend.uiwebsocket.api/bnd.bnd +++ /dev/null @@ -1,11 +0,0 @@ -Bundle-Name: OpenEMS Backend UiWebsocket Api -Bundle-Vendor: FENECON GmbH -Bundle-License: https://opensource.org/licenses/EPL-2.0 -Bundle-Version: 1.0.0.${tstamp} - --buildpath: \ - ${buildpath},\ - io.openems.common - --testpath: \ - ${testpath} diff --git a/io.openems.backend.uiwebsocket.api/test/.gitignore b/io.openems.backend.uiwebsocket.api/test/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/io.openems.backend.uiwebsocket.impl/.classpath b/io.openems.backend.uiwebsocket.impl/.classpath deleted file mode 100644 index 7a6fc254361..00000000000 --- a/io.openems.backend.uiwebsocket.impl/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/io.openems.backend.uiwebsocket.impl/.gitignore b/io.openems.backend.uiwebsocket.impl/.gitignore deleted file mode 100644 index 90dde36e4ac..00000000000 --- a/io.openems.backend.uiwebsocket.impl/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/bin/ -/bin_test/ -/generated/ diff --git a/io.openems.backend.uiwebsocket.impl/.project b/io.openems.backend.uiwebsocket.impl/.project deleted file mode 100644 index e8c7658ceec..00000000000 --- a/io.openems.backend.uiwebsocket.impl/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - io.openems.backend.uiwebsocket.impl - - - - - - org.eclipse.jdt.core.javabuilder - - - - - bndtools.core.bndbuilder - - - - - - org.eclipse.jdt.core.javanature - bndtools.core.bndnature - - diff --git a/io.openems.backend.uiwebsocket.impl/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.uiwebsocket.impl/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a21537071b..00000000000 --- a/io.openems.backend.uiwebsocket.impl/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/io.openems.backend.uiwebsocket.impl/test/.gitignore b/io.openems.backend.uiwebsocket.impl/test/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/io.openems.backend.edgewebsocket.impl/.classpath b/io.openems.backend.uiwebsocket/.classpath similarity index 100% rename from io.openems.backend.edgewebsocket.impl/.classpath rename to io.openems.backend.uiwebsocket/.classpath diff --git a/io.openems.backend.metadata.api/.gitignore b/io.openems.backend.uiwebsocket/.gitignore similarity index 100% rename from io.openems.backend.metadata.api/.gitignore rename to io.openems.backend.uiwebsocket/.gitignore diff --git a/io.openems.backend.timedata.api/.project b/io.openems.backend.uiwebsocket/.project similarity index 91% rename from io.openems.backend.timedata.api/.project rename to io.openems.backend.uiwebsocket/.project index 426049a9c28..01a01a35699 100644 --- a/io.openems.backend.timedata.api/.project +++ b/io.openems.backend.uiwebsocket/.project @@ -1,6 +1,6 @@ - io.openems.backend.timedata.api + io.openems.backend.uiwebsocket diff --git a/io.openems.backend.uiwebsocket.impl/.settings/org.eclipse.core.resources.prefs b/io.openems.backend.uiwebsocket/.settings/org.eclipse.core.resources.prefs similarity index 100% rename from io.openems.backend.uiwebsocket.impl/.settings/org.eclipse.core.resources.prefs rename to io.openems.backend.uiwebsocket/.settings/org.eclipse.core.resources.prefs diff --git a/io.openems.backend.edgewebsocket.impl/.settings/org.eclipse.jdt.core.prefs b/io.openems.backend.uiwebsocket/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from io.openems.backend.edgewebsocket.impl/.settings/org.eclipse.jdt.core.prefs rename to io.openems.backend.uiwebsocket/.settings/org.eclipse.jdt.core.prefs diff --git a/io.openems.backend.uiwebsocket.impl/bnd.bnd b/io.openems.backend.uiwebsocket/bnd.bnd similarity index 60% rename from io.openems.backend.uiwebsocket.impl/bnd.bnd rename to io.openems.backend.uiwebsocket/bnd.bnd index 933f6c36eb0..649b05c856e 100644 --- a/io.openems.backend.uiwebsocket.impl/bnd.bnd +++ b/io.openems.backend.uiwebsocket/bnd.bnd @@ -6,12 +6,8 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.backend.common,\ - io.openems.backend.edgewebsocket.api,\ - io.openems.backend.metadata.api,\ - io.openems.backend.timedata.api,\ - io.openems.backend.uiwebsocket.api,\ io.openems.common,\ - Java-WebSocket;version=1.4.1 + Java-WebSocket;version='1.4.1' -testpath: \ ${testpath} diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/Config.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/Config.java similarity index 64% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/Config.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/Config.java index 48e88279529..1a2ad672dfc 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/Config.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/Config.java @@ -11,6 +11,12 @@ @AttributeDefinition(name = "Port", description = "The port of the websocket server.") int port() default 8082; + @AttributeDefinition(name = "Number of Threads", description = "Pool-Size: the number of threads dedicated to handle the tasks") + int poolSize() default 10; + + @AttributeDefinition(name = "Debug Mode", description = "Activates the debug mode") + boolean debugMode() default false; + String webconsole_configurationFactory_nameHint() default "Ui Websocket"; } diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnClose.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnClose.java similarity index 94% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnClose.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnClose.java index b1690fd0c7a..0f89c2a7fc7 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnClose.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnClose.java @@ -6,7 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsException; public class OnClose implements io.openems.common.websocket.OnClose { diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnError.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnError.java similarity index 100% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnError.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnError.java diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnNotification.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnNotification.java similarity index 100% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnNotification.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnNotification.java diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnOpen.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnOpen.java similarity index 59% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnOpen.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnOpen.java index a64b6bcc913..88925b064dd 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnOpen.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnOpen.java @@ -1,8 +1,5 @@ package io.openems.backend.uiwebsocket.impl; -import java.util.ArrayList; -import java.util.List; -import java.util.Map.Entry; import java.util.Optional; import java.util.UUID; @@ -12,14 +9,11 @@ import com.google.gson.JsonObject; -import io.openems.backend.metadata.api.Edge; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.notification.AuthenticateWithSessionIdFailedNotification; import io.openems.common.jsonrpc.notification.AuthenticateWithSessionIdNotification; -import io.openems.common.jsonrpc.shared.EdgeMetadata; -import io.openems.common.session.Role; public class OnOpen implements io.openems.common.websocket.OnOpen { @@ -38,16 +32,22 @@ public void run(WebSocket ws, JsonObject handshake) throws OpenemsException { // declare user BackendUser user; - // login using session_id from the handshake - Optional sessionIdOpt = io.openems.common.websocket.OnOpen.getFieldFromHandshakeCookie(handshake, - "session_id"); try { - if (sessionIdOpt.isPresent()) { - // authenticate with Session-ID - user = this.parent.metadata.authenticate(sessionIdOpt.get()); + // authenticate with Token + Optional tokenOpt = io.openems.common.websocket.OnOpen.getFieldFromHandshakeCookie(handshake, + "token"); + if (tokenOpt.isPresent()) { + user = this.parent.metadata.authenticate(tokenOpt.get()); } else { - // authenticate without Session-ID - user = this.parent.metadata.authenticate(); + // authenticate with Session-ID + Optional sessionIdOpt = io.openems.common.websocket.OnOpen + .getFieldFromHandshakeCookie(handshake, "session_id"); + if (sessionIdOpt.isPresent()) { + user = this.parent.metadata.authenticate(sessionIdOpt.get()); + } else { + // authenticate without Session-ID + user = this.parent.metadata.authenticate(); + } } } catch (OpenemsNamedException e) { // login using session_id failed. Still keeping the WebSocket opened to give the @@ -68,26 +68,8 @@ public void run(WebSocket ws, JsonObject handshake) throws OpenemsException { wsData.setToken(token); // send connection successful reply - List metadatas = new ArrayList<>(); - for (Entry edgeRole : user.getEdgeRoles().entrySet()) { - String edgeId = edgeRole.getKey(); - Role role = edgeRole.getValue(); - Optional edgeOpt = this.parent.metadata.getEdge(edgeId); - if (edgeOpt.isPresent()) { - Edge e = edgeOpt.get(); - metadatas.add(new EdgeMetadata(// - e.getId(), // Edge-ID - e.getComment(), // Comment - e.getProducttype(), // Product-Type - e.getVersion(), // Version - role, // Role - e.isOnline() // Online-State - )); - } - } - - AuthenticateWithSessionIdNotification notification = new AuthenticateWithSessionIdNotification(token, - metadatas); + AuthenticateWithSessionIdNotification notification = new AuthenticateWithSessionIdNotification( + user.getSessionId(), user.getEdgeMetadatas(this.parent.metadata)); this.parent.server.sendMessage(ws, notification); this.parent.logInfo(this.log, "User [" + user.getId() + ":" + user.getName() + "] connected."); diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnRequest.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnRequest.java similarity index 79% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnRequest.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnRequest.java index 1440cee6b61..e6acf3c6d87 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/OnRequest.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnRequest.java @@ -6,15 +6,17 @@ import org.java_websocket.WebSocket; -import io.openems.backend.metadata.api.BackendUser; +import io.openems.backend.common.metadata.BackendUser; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.AuthenticateWithPasswordRequest; import io.openems.common.jsonrpc.request.EdgeRpcRequest; import io.openems.common.jsonrpc.request.SubscribeChannelsRequest; import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; +import io.openems.common.jsonrpc.response.AuthenticateWithPasswordResponse; import io.openems.common.jsonrpc.response.EdgeRpcResponse; import io.openems.common.session.Role; import io.openems.common.session.User; @@ -31,9 +33,16 @@ public OnRequest(UiWebsocketImpl parent) { public CompletableFuture run(WebSocket ws, JsonrpcRequest request) throws OpenemsNamedException { WsData wsData = ws.getAttachment(); + + CompletableFuture result = null; + if (request.getMethod().equals(AuthenticateWithPasswordRequest.METHOD)) { + // trying to authenticate + return this.handleAuthenticateWithPasswordRequest(wsData, AuthenticateWithPasswordRequest.from(request)); + } + + // should be authenticated BackendUser user = this.assertUser(wsData, request); - CompletableFuture result = null; switch (request.getMethod()) { case EdgeRpcRequest.METHOD: result = this.handleEdgeRpcRequest(wsData, user, EdgeRpcRequest.from(request)); @@ -48,6 +57,28 @@ public CompletableFuture run(WebSocket ws, Jso return this.parent.jsonRpcRequestHandler.handleRequest(this.parent.getName(), user, request); } + /** + * Handles an AuthenticateWithPasswordRequest. + * + * @param wsData the WebSocket attachment + * @param backendUser the authenticated User + * @param request the AuthenticateWithUsernameAndPasswordRequest + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleAuthenticateWithPasswordRequest(WsData wsData, + AuthenticateWithPasswordRequest request) throws OpenemsNamedException { + BackendUser user; + if (request.getUsername().isPresent()) { + user = this.parent.metadata.authenticate(request.getUsername().get(), request.getPassword()); + } else { + user = this.parent.metadata.authenticate(request.getPassword()); + } + wsData.setUserId(user.getId()); + return CompletableFuture.completedFuture(new AuthenticateWithPasswordResponse(request.getId(), + user.getSessionId(), user.getEdgeMetadatas(this.parent.metadata))); + } + /** * Gets the authenticated User or throws an Exception if User is not * authenticated. diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/SubscribedChannelsWorker.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/SubscribedChannelsWorker.java similarity index 100% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/SubscribedChannelsWorker.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/SubscribedChannelsWorker.java diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java similarity index 81% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java index 6281f8dcc96..cef82be2ba6 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java @@ -18,12 +18,12 @@ import org.slf4j.Logger; import io.openems.backend.common.component.AbstractOpenemsBackendComponent; +import io.openems.backend.common.edgewebsocket.EdgeWebsocket; import io.openems.backend.common.jsonrpc.JsonRpcRequestHandler; -import io.openems.backend.edgewebsocket.api.EdgeWebsocket; -import io.openems.backend.metadata.api.BackendUser; -import io.openems.backend.metadata.api.Metadata; -import io.openems.backend.timedata.api.Timedata; -import io.openems.backend.uiwebsocket.api.UiWebsocket; +import io.openems.backend.common.metadata.BackendUser; +import io.openems.backend.common.metadata.Metadata; +import io.openems.backend.common.timedata.Timedata; +import io.openems.backend.common.uiwebsocket.UiWebsocket; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.JsonrpcNotification; @@ -32,7 +32,11 @@ import io.openems.common.session.Role; @Designate(ocd = Config.class, factory = false) -@Component(name = "Ui.Websocket", configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true) +@Component(// + name = "Ui.Websocket", // + configurationPolicy = ConfigurationPolicy.REQUIRE, // + immediate = true // +) public class UiWebsocketImpl extends AbstractOpenemsBackendComponent implements UiWebsocket { // private final Logger log = LoggerFactory.getLogger(UiWebsocket.class); @@ -55,23 +59,33 @@ public UiWebsocketImpl() { super("Ui.Websocket"); } + private Config config; + + private final Runnable startServerWhenMetadataIsInitialized = () -> { + this.startServer(config.port(), config.poolSize(), config.debugMode()); + }; + @Activate void activate(Config config) { - this.startServer(config.port()); + this.config = config; + this.metadata.addOnIsInitializedListener(this.startServerWhenMetadataIsInitialized); } @Deactivate void deactivate() { + this.metadata.removeOnIsInitializedListener(this.startServerWhenMetadataIsInitialized); this.stopServer(); } /** * Create and start new server. * - * @param port the port + * @param port the port + * @param poolSize number of threads dedicated to handle the tasks + * @param debugMode activate a regular debug log about the state of the tasks */ - private synchronized void startServer(int port) { - this.server = new WebsocketServer(this, "Ui.Websocket", port); + private synchronized void startServer(int port, int poolSize, boolean debugMode) { + this.server = new WebsocketServer(this, "Ui.Websocket", port, poolSize, debugMode); this.server.start(); } diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/WebsocketServer.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/WebsocketServer.java similarity index 95% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/WebsocketServer.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/WebsocketServer.java index 434c28c4f40..6cc6ee144ad 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/WebsocketServer.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/WebsocketServer.java @@ -19,8 +19,8 @@ public class WebsocketServer extends AbstractWebsocketServer { private final OnError onError; private final OnClose onClose; - public WebsocketServer(UiWebsocketImpl parent, String name, int port) { - super(name, port); + public WebsocketServer(UiWebsocketImpl parent, String name, int port, int poolSize, boolean debugMode) { + super(name, port, poolSize, debugMode); this.parent = parent; this.onOpen = new OnOpen(parent); this.onRequest = new OnRequest(parent); diff --git a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/WsData.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/WsData.java similarity index 96% rename from io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/WsData.java rename to io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/WsData.java index 65ed4f4bd65..13df335428e 100644 --- a/io.openems.backend.uiwebsocket.impl/src/io/openems/backend/uiwebsocket/impl/WsData.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/WsData.java @@ -5,8 +5,8 @@ import java.util.Optional; import java.util.UUID; -import io.openems.backend.metadata.api.BackendUser; -import io.openems.backend.metadata.api.Metadata; +import io.openems.backend.common.metadata.BackendUser; +import io.openems.backend.common.metadata.Metadata; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; diff --git a/io.openems.backend.edgewebsocket.impl/test/.gitignore b/io.openems.backend.uiwebsocket/test/.gitignore similarity index 100% rename from io.openems.backend.edgewebsocket.impl/test/.gitignore rename to io.openems.backend.uiwebsocket/test/.gitignore diff --git a/io.openems.common/.settings/org.eclipse.core.resources.prefs b/io.openems.common/.settings/org.eclipse.core.resources.prefs index 996d790e695..ee184c2791a 100644 --- a/io.openems.common/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.common/.settings/org.eclipse.core.resources.prefs @@ -1,4 +1,5 @@ eclipse.preferences.version=1 +encoding//src/io/openems/common/channel/PersistencePriority.java=UTF-8 encoding//src/io/openems/common/channel/Unit.java=UTF-8 encoding//test/.gitignore=UTF-8 encoding/bnd.bnd=UTF-8 diff --git a/io.openems.common/src/io/openems/common/OpenemsConstants.java b/io.openems.common/src/io/openems/common/OpenemsConstants.java index 8d3fcc55b0a..40d4c5193ef 100644 --- a/io.openems.common/src/io/openems/common/OpenemsConstants.java +++ b/io.openems.common/src/io/openems/common/OpenemsConstants.java @@ -20,7 +20,7 @@ public class OpenemsConstants { * * This is usually the number of the sprint within the year */ - public final static short VERSION_MINOR = 4; + public final static short VERSION_MINOR = 5; /** * The patch version of OpenEMS. diff --git a/io.openems.common/src/io/openems/common/channel/PersistencePriority.java b/io.openems.common/src/io/openems/common/channel/PersistencePriority.java new file mode 100644 index 00000000000..95ef9696426 --- /dev/null +++ b/io.openems.common/src/io/openems/common/channel/PersistencePriority.java @@ -0,0 +1,26 @@ +package io.openems.common.channel; + +public enum PersistencePriority { + + VERY_LOW(0), // + LOW(1), // + MEDIUM(2), // + HIGH(3), // + VERY_HIGH(4), // + ; + + private final int value; + + private PersistencePriority(int value) { + this.value = value; + } + + public boolean isAtLeast(PersistencePriority other) { + return this.value >= other.value; + } + + public boolean isLowerThan(PersistencePriority other) { + return this.value < other.value; + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/base/GenericJsonrpcRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/base/GenericJsonrpcRequest.java index 8092cac7393..7c836992ea3 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/base/GenericJsonrpcRequest.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/base/GenericJsonrpcRequest.java @@ -1,5 +1,6 @@ package io.openems.common.jsonrpc.base; +import java.util.Optional; import java.util.UUID; import com.google.gson.JsonObject; @@ -15,7 +16,8 @@ * "jsonrpc": "2.0", * "id": "UUID", * "method": string, - * "params": {} + * "params": {}, + * "timeout"?: number, defaults to 60 seconds; negative or zero to disable timeout * } * * @@ -27,7 +29,7 @@ public class GenericJsonrpcRequest extends JsonrpcRequest { /** * Parses the String to a {@link GenericJsonrpcRequest}. * - * @param j the String + * @param json the String * @return the {@link GenericJsonrpcRequest} * @throws OpenemsNamedException on error */ @@ -36,29 +38,30 @@ public static GenericJsonrpcRequest from(String json) throws OpenemsNamedExcepti } /** - * Parses the String to a {@link GenericJsonrpcRequest}. If the request UUID is - * missing, it is replaced by a random UUID. + * Parses the {@link JsonObject} to a {@link GenericJsonrpcRequest}. * - * @param j the String + * @param j the {@link JsonObject} * @return the {@link GenericJsonrpcRequest} * @throws OpenemsNamedException on error */ - public static GenericJsonrpcRequest fromIgnoreId(String json) throws OpenemsNamedException { - return fromIgnoreId(JsonUtils.parseToJsonObject(json)); + public static GenericJsonrpcRequest from(JsonObject j) throws OpenemsNamedException { + UUID id = JsonUtils.getAsUUID(j, "id"); + String method = JsonUtils.getAsString(j, "method"); + JsonObject params = JsonUtils.getAsJsonObject(j, "params"); + Optional timeout = JsonUtils.getAsOptionalInt(j, "timeout"); + return new GenericJsonrpcRequest(id, method, params, timeout); } /** - * Parses the {@link JsonObject} to a {@link GenericJsonrpcRequest}. + * Parses the String to a {@link GenericJsonrpcRequest}. If the request UUID is + * missing, it is replaced by a random UUID. * - * @param j the {@link JsonObject} + * @param json the String * @return the {@link GenericJsonrpcRequest} * @throws OpenemsNamedException on error */ - public static GenericJsonrpcRequest from(JsonObject j) throws OpenemsNamedException { - UUID id = JsonUtils.getAsUUID(j, "id"); - String method = JsonUtils.getAsString(j, "method"); - JsonObject params = JsonUtils.getAsJsonObject(j, "params"); - return new GenericJsonrpcRequest(id, method, params); + public static GenericJsonrpcRequest fromIgnoreId(String json) throws OpenemsNamedException { + return fromIgnoreId(JsonUtils.parseToJsonObject(json)); } /** @@ -73,13 +76,19 @@ public static GenericJsonrpcRequest fromIgnoreId(JsonObject j) throws OpenemsNam UUID id = JsonUtils.getAsOptionalUUID(j, "id").orElse(new UUID(0L, 0L) /* dummy UUID */); String method = JsonUtils.getAsString(j, "method"); JsonObject params = JsonUtils.getAsJsonObject(j, "params"); - return new GenericJsonrpcRequest(id, method, params); + Optional timeout = JsonUtils.getAsOptionalInt(j, "timeout"); + return new GenericJsonrpcRequest(id, method, params, timeout); } private final JsonObject params; - public GenericJsonrpcRequest(UUID id, String method, JsonObject params) { - super(id, method); + public GenericJsonrpcRequest(UUID id, String method, JsonObject params, int timeout) { + super(id, method, timeout); + this.params = params; + } + + public GenericJsonrpcRequest(UUID id, String method, JsonObject params, Optional timeout) { + super(id, method, timeout); this.params = params; } diff --git a/io.openems.common/src/io/openems/common/jsonrpc/base/JsonrpcRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/base/JsonrpcRequest.java index df10bd21658..f89d0476c28 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/base/JsonrpcRequest.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/base/JsonrpcRequest.java @@ -1,9 +1,12 @@ package io.openems.common.jsonrpc.base; +import java.util.Optional; import java.util.UUID; + import com.google.gson.JsonObject; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.JsonUtils.JsonObjectBuilder; /** * Represents a JSON-RPC Request. @@ -13,7 +16,8 @@ * "jsonrpc": "2.0", * "id": "UUID", * "method": string, - * "params": {} + * "params": {}, + * "timeout"?: number, defaults to 60 seconds; negative or zero to disable timeout * } * * @@ -22,25 +26,117 @@ */ public abstract class JsonrpcRequest extends AbstractJsonrpcRequest { + public static final int DEFAULT_TIMEOUT_SECONDS = 60; + public static final int NO_TIMEOUT = -1; + private final UUID id; + private final Optional timeout; + /** + * Creates a {@link JsonrpcRequest} with random {@link UUID} as id and + * {@link #DEFAULT_TIMEOUT_SECONDS} timeout. + * + * @param method the JSON-RPC method + */ public JsonrpcRequest(String method) { - this(UUID.randomUUID(), method); + this(method, Optional.empty()); + } + + /** + * Creates a {@link JsonrpcRequest} with random {@link UUID} as id. + * + * @param method the JSON-RPC method + * @param timeout max time in seconds to wait for the {@link JsonrpcResponse}, + * negative or zero to disable timeout + */ + public JsonrpcRequest(String method, int timeout) { + this(method, Optional.of(timeout)); + } + + /** + * Creates a {@link JsonrpcRequest} with random {@link UUID} as id. + * + * @param method the JSON-RPC method + * @param timeout max time in seconds to wait for the {@link JsonrpcResponse}, + * negative or zero to disable timeout, empty for + * {@link #DEFAULT_TIMEOUT_SECONDS} timeout + */ + public JsonrpcRequest(String method, Optional timeout) { + this(UUID.randomUUID(), method, timeout); } - public JsonrpcRequest(UUID id, String method) { + /** + * Creates a {@link JsonrpcRequest}. + * + * @param id the JSON-RPC id + * @param method the JSON-RPC method + * @param timeout max time in seconds to wait for the {@link JsonrpcResponse}, + * negative or zero to disable timeout, empty for + * {@link #DEFAULT_TIMEOUT_SECONDS} timeout + */ + public JsonrpcRequest(UUID id, String method, Optional timeout) { super(method); this.id = id; + this.timeout = timeout; } + /** + * Creates a {@link JsonrpcRequest} by copying and validating header + * information. + * + *
    + *
  • copies id and timeout + *
  • validates that the method names match + *
+ * + * @param request the template JSON-RPC Request + * @param method the JSON-RPC method + */ + protected JsonrpcRequest(JsonrpcRequest request, String method) { + this(request.id, method, request.timeout); + if (!request.getMethod().equals(method)) { + throw new IllegalArgumentException("JSON-RPC Methods to not match: " + request.getMethod() + ", " + method); + } + } + + /** + * Creates a {@link JsonrpcRequest}. + * + * @param id the JSON-RPC id + * @param method the JSON-RPC method + * @param timeout max time in seconds to wait for the {@link JsonrpcResponse}, + * negative or zero to disable timeout + */ + public JsonrpcRequest(UUID id, String method, int timeout) { + this(id, method, Optional.of(timeout)); + } + + /** + * Gets the JSON-RPC id. + * + * @return the {@link UUID} id + */ public UUID getId() { return this.id; } + /** + * Gets the max time in seconds to wait for the {@link JsonrpcResponse}, + * negative or zero to disable timeout. + * + * @return the timeout in seconds + */ + public Optional getTimeout() { + return this.timeout; + } + @Override public JsonObject toJsonObject() { - return JsonUtils.buildJsonObject(super.toJsonObject()) // - .addProperty("id", this.getId().toString()) // - .build(); + JsonObjectBuilder builder = JsonUtils.buildJsonObject(super.toJsonObject()) // + .addProperty("id", this.getId().toString()); + if (this.timeout.isPresent()) { + builder.addProperty("timeout", this.timeout.get()); + } + return builder.build(); } } \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/jsonrpc/notification/AuthenticateWithSessionIdNotification.java b/io.openems.common/src/io/openems/common/jsonrpc/notification/AuthenticateWithSessionIdNotification.java index d34171bbeee..dabfb8a7525 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/notification/AuthenticateWithSessionIdNotification.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/notification/AuthenticateWithSessionIdNotification.java @@ -1,7 +1,6 @@ package io.openems.common.jsonrpc.notification; import java.util.List; -import java.util.UUID; import com.google.gson.JsonObject; @@ -17,7 +16,7 @@ * "jsonrpc": "2.0", * "method": "authenticatedWithSessionId", * "params": { - * "token": UUID, + * "token": String, * "edges": {@link EdgeMetadata#toJson(java.util.Collection)} * } * } @@ -27,10 +26,10 @@ public class AuthenticateWithSessionIdNotification extends JsonrpcNotification { public final static String METHOD = "authenticatedWithSessionId"; - private final UUID token; + private final String token; private final List metadatas; - public AuthenticateWithSessionIdNotification(UUID token, List metadatas) { + public AuthenticateWithSessionIdNotification(String token, List metadatas) { super(METHOD); this.token = token; this.metadatas = metadatas; @@ -39,17 +38,17 @@ public AuthenticateWithSessionIdNotification(UUID token, List meta @Override public JsonObject getParams() { return JsonUtils.buildJsonObject() // - .addProperty("token", this.token.toString()) // + .addProperty("token", this.token) // .add("edges", EdgeMetadata.toJson(this.metadatas)) // .build(); } - public UUID getToken() { - return token; + public String getToken() { + return this.token; } public List getMetadatas() { - return metadatas; + return this.metadatas; } } diff --git a/io.openems.common/src/io/openems/common/jsonrpc/notification/TimestampedDataNotification.java b/io.openems.common/src/io/openems/common/jsonrpc/notification/TimestampedDataNotification.java index 1bfbca56ce4..cc00eaa4525 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/notification/TimestampedDataNotification.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/notification/TimestampedDataNotification.java @@ -21,7 +21,7 @@ * "jsonrpc": "2.0", * "method": "timestampedData", * "params": { - * [timestamp: epoch in seconds]: { + * [timestamp: epoch in milliseconds]: { * [channelAddress]: String | Number * } * } @@ -77,6 +77,6 @@ public JsonObject getParams() { } public TreeBasedTable getData() { - return data; + return this.data; } } diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticateWithPasswordRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticateWithPasswordRequest.java index f707e3ba2c4..65d9e4b1f28 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticateWithPasswordRequest.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticateWithPasswordRequest.java @@ -1,17 +1,18 @@ package io.openems.common.jsonrpc.request; -import java.util.UUID; +import java.util.Optional; import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.utils.JsonUtils; +import io.openems.common.utils.JsonUtils.JsonObjectBuilder; /** * Represents a JSON-RPC Request to authenticate with a Password. * + *

* This is used by UI to login with password-only at the Edge. * *

@@ -20,6 +21,7 @@
  *   "id": "UUID",
  *   "method": "authenticateWithPassword",
  *   "params": {
+ *     "username"?: string,
  *     "password": string
  *   }
  * }
@@ -27,37 +29,63 @@
  */
 public class AuthenticateWithPasswordRequest extends JsonrpcRequest {
 
-	public final static String METHOD = "authenticateWithPassword";
+	public static final String METHOD = "authenticateWithPassword";
 
+	/**
+	 * Create {@link AuthenticateWithPasswordRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link AuthenticateWithPasswordRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static AuthenticateWithPasswordRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
+		Optional username = JsonUtils.getAsOptionalString(p, "username");
 		String password = JsonUtils.getAsString(p, "password");
-		return new AuthenticateWithPasswordRequest(r.getId(), password);
-	}
-
-	public static AuthenticateWithPasswordRequest from(JsonObject j) throws OpenemsNamedException {
-		return from(GenericJsonrpcRequest.from(j));
+		return new AuthenticateWithPasswordRequest(r, username, password);
 	}
 
+	private final Optional username;
 	private final String password;
 
-	public AuthenticateWithPasswordRequest(UUID id, String password) {
-		super(id, METHOD);
+	private AuthenticateWithPasswordRequest(JsonrpcRequest request, Optional username, String password) {
+		super(request, METHOD);
+		this.username = username;
+		this.password = password;
+	}
+
+	public AuthenticateWithPasswordRequest(Optional username, String password) {
+		super(METHOD);
+		this.username = username;
 		this.password = password;
 	}
 
-	public AuthenticateWithPasswordRequest(String password) {
-		this(UUID.randomUUID(), password);
+	/**
+	 * Gets the Username if given.
+	 * 
+	 * @return Username
+	 */
+	public Optional getUsername() {
+		return this.username;
 	}
 
+	/**
+	 * Gets the Password.
+	 * 
+	 * @return Password
+	 */
 	public String getPassword() {
-		return password;
+		return this.password;
 	}
 
 	@Override
 	public JsonObject getParams() {
-		return JsonUtils.buildJsonObject() //
-				.addProperty("password", this.password) //
-				.build();
+		JsonObjectBuilder result = JsonUtils.buildJsonObject() //
+				.addProperty("password", this.password); //
+		if (this.username.isPresent()) {
+			result.addProperty("username", this.username.get()); //
+		}
+		return result.build();
 	}
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticatedRpcRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticatedRpcRequest.java
index ce98cc312a5..08f8d6e6482 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticatedRpcRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/AuthenticatedRpcRequest.java
@@ -1,7 +1,5 @@
 package io.openems.common.jsonrpc.request;
 
-import java.util.UUID;
-
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
@@ -31,34 +29,54 @@
  */
 public class AuthenticatedRpcRequest extends JsonrpcRequest {
 
-	public final static String METHOD = "authenticatedRpc";
+	public static final String METHOD = "authenticatedRpc";
 
+	/**
+	 * Create {@link AuthenticatedRpcRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link AuthenticatedRpcRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static AuthenticatedRpcRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		User user = User.from(JsonUtils.getAsJsonObject(p, "user"));
 		JsonrpcRequest payload = GenericJsonrpcRequest.from(JsonUtils.getAsJsonObject(p, "payload"));
-		return new AuthenticatedRpcRequest(r.getId(), user, payload);
+		return new AuthenticatedRpcRequest(r, user, payload);
 	}
 
 	private final User user;
 	private final JsonrpcRequest payload;
 
 	public AuthenticatedRpcRequest(User user, JsonrpcRequest payload) {
-		this(UUID.randomUUID(), user, payload);
+		super(METHOD);
+		this.user = user;
+		this.payload = payload;
 	}
 
-	public AuthenticatedRpcRequest(UUID id, User user, JsonrpcRequest payload) {
-		super(id, METHOD);
+	private AuthenticatedRpcRequest(JsonrpcRequest request, User user, JsonrpcRequest payload) {
+		super(request, METHOD);
 		this.user = user;
 		this.payload = payload;
 	}
 
+	/**
+	 * Gets the {@link User}.
+	 * 
+	 * @return User
+	 */
 	public User getUser() {
-		return user;
+		return this.user;
 	}
 
+	/**
+	 * Gets the Payload {@link JsonrpcRequest}.
+	 * 
+	 * @return Payload
+	 */
 	public JsonrpcRequest getPayload() {
-		return payload;
+		return this.payload;
 	}
 
 	@Override
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/ComponentJsonApiRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/ComponentJsonApiRequest.java
index 02d95ad6460..6de9d8837ca 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/ComponentJsonApiRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/ComponentJsonApiRequest.java
@@ -1,7 +1,5 @@
 package io.openems.common.jsonrpc.request;
 
-import java.util.UUID;
-
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
@@ -10,7 +8,7 @@
 import io.openems.common.utils.JsonUtils;
 
 /**
- * Wraps a JSON-RPC Request for an OpenEMS Component that implements JsonApi
+ * Wraps a JSON-RPC Request for an OpenEMS Component that implements JsonApi.
  * 
  * 
  * {
@@ -26,24 +24,34 @@
  */
 public class ComponentJsonApiRequest extends JsonrpcRequest {
 
+	public static final String METHOD = "componentJsonApi";
+
+	/**
+	 * Create {@link ComponentJsonApiRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link ComponentJsonApiRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static ComponentJsonApiRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		String componentId = JsonUtils.getAsString(p, "componentId");
 		JsonrpcRequest payload = GenericJsonrpcRequest.fromIgnoreId(JsonUtils.getAsJsonObject(p, "payload"));
-		return new ComponentJsonApiRequest(r.getId(), componentId, payload);
+		return new ComponentJsonApiRequest(r, componentId, payload);
 	}
 
-	public final static String METHOD = "componentJsonApi";
-
 	private final String componentId;
 	private final JsonrpcRequest payload;
 
 	public ComponentJsonApiRequest(String componentId, JsonrpcRequest payload) {
-		this(UUID.randomUUID(), componentId, payload);
+		super(METHOD, payload.getTimeout() /* inherit timeout from payload */);
+		this.componentId = componentId;
+		this.payload = payload;
 	}
 
-	public ComponentJsonApiRequest(UUID id, String componentId, JsonrpcRequest payload) {
-		super(id, METHOD);
+	private ComponentJsonApiRequest(JsonrpcRequest request, String componentId, JsonrpcRequest payload) {
+		super(request, METHOD);
 		this.componentId = componentId;
 		this.payload = payload;
 	}
@@ -56,12 +64,22 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the Component-ID.
+	 * 
+	 * @return Component-ID
+	 */
 	public String getComponentId() {
-		return componentId;
+		return this.componentId;
 	}
 
+	/**
+	 * Gets the Payload {@link JsonrpcRequest}.
+	 * 
+	 * @return Payload
+	 */
 	public JsonrpcRequest getPayload() {
-		return payload;
+		return this.payload;
 	}
 
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/CreateComponentConfigRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/CreateComponentConfigRequest.java
index 616ab243718..2b467a31b79 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/CreateComponentConfigRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/CreateComponentConfigRequest.java
@@ -1,7 +1,6 @@
 package io.openems.common.jsonrpc.request;
 
 import java.util.List;
-import java.util.UUID;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
@@ -31,33 +30,47 @@
  */
 public class CreateComponentConfigRequest extends JsonrpcRequest {
 
-	public static CreateComponentConfigRequest from(JsonrpcRequest r) throws OpenemsNamedException {
-		return CreateComponentConfigRequest.from(r.getId(), r.getParams());
-	}
+	public static final String METHOD = "createComponentConfig";
 
-	public static CreateComponentConfigRequest from(UUID id, JsonObject params) throws OpenemsNamedException {
-		String factoryPid = JsonUtils.getAsString(params, "factoryPid");
-		List properties = Property.from(JsonUtils.getAsJsonArray(params, "properties"));
-		return new CreateComponentConfigRequest(id, factoryPid, properties);
+	/**
+	 * Create {@link CreateComponentConfigRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link CreateComponentConfigRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
+	public static CreateComponentConfigRequest from(JsonrpcRequest r) throws OpenemsNamedException {
+		JsonObject p = r.getParams();
+		String factoryPid = JsonUtils.getAsString(p, "factoryPid");
+		List properties = Property.from(JsonUtils.getAsJsonArray(p, "properties"));
+		return new CreateComponentConfigRequest(r, factoryPid, properties);
 	}
 
+	/**
+	 * Create {@link CreateComponentConfigRequest} from a {@link JsonObject}.
+	 * 
+	 * @param params the {@link JsonObject}
+	 * @return the {@link CreateComponentConfigRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static CreateComponentConfigRequest from(JsonObject params) throws OpenemsNamedException {
 		String factoryPid = JsonUtils.getAsString(params, "factoryPid");
 		List properties = Property.from(JsonUtils.getAsJsonArray(params, "properties"));
 		return new CreateComponentConfigRequest(factoryPid, properties);
 	}
 
-	public final static String METHOD = "createComponentConfig";
-
 	private final String factoryPid;
 	private final List properties;
 
 	public CreateComponentConfigRequest(String factoryPid, List properties) {
-		this(UUID.randomUUID(), factoryPid, properties);
+		super(METHOD);
+		this.factoryPid = factoryPid;
+		this.properties = properties;
 	}
 
-	public CreateComponentConfigRequest(UUID id, String factoryPid, List properties) {
-		super(id, METHOD);
+	private CreateComponentConfigRequest(JsonrpcRequest request, String factoryPid, List properties) {
+		super(request, METHOD);
 		this.factoryPid = factoryPid;
 		this.properties = properties;
 	}
@@ -74,10 +87,20 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the Factory-PID.
+	 * 
+	 * @return Factory-PID
+	 */
 	public String getFactoryPid() {
-		return factoryPid;
+		return this.factoryPid;
 	}
 
+	/**
+	 * Gets the Component-ID, or empty String if none is given.
+	 * 
+	 * @return Component-ID
+	 */
 	public String getComponentId() {
 		for (UpdateComponentConfigRequest.Property property : this.properties) {
 			if (property.getName().equals("id")) {
@@ -87,7 +110,12 @@ public String getComponentId() {
 		return "";
 	}
 
+	/**
+	 * Gets the List of Properties.
+	 * 
+	 * @return Properties
+	 */
 	public List getProperties() {
-		return properties;
+		return this.properties;
 	}
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/DeleteComponentConfigRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/DeleteComponentConfigRequest.java
index dffa8199573..3ce2f9080f8 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/DeleteComponentConfigRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/DeleteComponentConfigRequest.java
@@ -1,7 +1,5 @@
 package io.openems.common.jsonrpc.request;
 
-import java.util.UUID;
-
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
@@ -24,22 +22,31 @@
  */
 public class DeleteComponentConfigRequest extends JsonrpcRequest {
 
+	public static final String METHOD = "deleteComponentConfig";
+
+	/**
+	 * Create {@link DeleteComponentConfigRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link DeleteComponentConfigRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static DeleteComponentConfigRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		String componentId = JsonUtils.getAsString(p, "componentId");
-		return new DeleteComponentConfigRequest(r.getId(), componentId);
+		return new DeleteComponentConfigRequest(r, componentId);
 	}
 
-	public final static String METHOD = "deleteComponentConfig";
-
 	private final String componentId;
 
 	public DeleteComponentConfigRequest(String componentId) {
-		this(UUID.randomUUID(), componentId);
+		super(METHOD);
+		this.componentId = componentId;
 	}
 
-	public DeleteComponentConfigRequest(UUID id, String componentId) {
-		super(id, METHOD);
+	private DeleteComponentConfigRequest(JsonrpcRequest request, String componentId) {
+		super(request, METHOD);
 		this.componentId = componentId;
 	}
 
@@ -50,7 +57,12 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the Component-ID.
+	 * 
+	 * @return Component-ID
+	 */
 	public String getComponentId() {
-		return componentId;
+		return this.componentId;
 	}
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/EdgeRpcRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/EdgeRpcRequest.java
index 34e2f8a2729..f815c05b4f8 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/EdgeRpcRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/EdgeRpcRequest.java
@@ -1,7 +1,5 @@
 package io.openems.common.jsonrpc.request;
 
-import java.util.UUID;
-
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
@@ -27,34 +25,53 @@
  */
 public class EdgeRpcRequest extends JsonrpcRequest {
 
-	public final static String METHOD = "edgeRpc";
+	public static final String METHOD = "edgeRpc";
 
+	/**
+	 * Create {@link EdgeRpcRequest} from a template {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link EdgeRpcRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static EdgeRpcRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		String edgeId = JsonUtils.getAsString(p, "edgeId");
 		JsonrpcRequest payload = GenericJsonrpcRequest.from(JsonUtils.getAsJsonObject(p, "payload"));
-		return new EdgeRpcRequest(r.getId(), edgeId, payload);
+		return new EdgeRpcRequest(r, edgeId, payload);
 	}
 
 	private final String edgeId;
 	private final JsonrpcRequest payload;
 
 	public EdgeRpcRequest(String edgeId, JsonrpcRequest payload) {
-		this(UUID.randomUUID(), edgeId, payload);
+		super(METHOD, payload.getTimeout() /* inherit timeout from payload */);
+		this.edgeId = edgeId;
+		this.payload = payload;
 	}
 
-	public EdgeRpcRequest(UUID id, String edgeId, JsonrpcRequest payload) {
-		super(id, METHOD);
+	public EdgeRpcRequest(JsonrpcRequest request, String edgeId, JsonrpcRequest payload) {
+		super(request, METHOD);
 		this.edgeId = edgeId;
 		this.payload = payload;
 	}
 
+	/**
+	 * Gets the Edge-ID.
+	 * 
+	 * @return Edge-ID
+	 */
 	public String getEdgeId() {
-		return edgeId;
+		return this.edgeId;
 	}
 
+	/**
+	 * Gets the Payload {@link JsonrpcRequest}.
+	 * 
+	 * @return Payload
+	 */
 	public JsonrpcRequest getPayload() {
-		return payload;
+		return this.payload;
 	}
 
 	@Override
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/GetEdgeConfigRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/GetEdgeConfigRequest.java
index 1a8ae619f83..f775ba6ae60 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/GetEdgeConfigRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/GetEdgeConfigRequest.java
@@ -1,9 +1,8 @@
 package io.openems.common.jsonrpc.request;
 
-import java.util.UUID;
-
 import com.google.gson.JsonObject;
 
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
 import io.openems.common.exceptions.OpenemsException;
 import io.openems.common.jsonrpc.base.JsonrpcRequest;
 
@@ -21,18 +20,21 @@
  */
 public class GetEdgeConfigRequest extends JsonrpcRequest {
 
-	public static GetEdgeConfigRequest from(JsonrpcRequest r) throws OpenemsException {
-		return new GetEdgeConfigRequest(r.getId());
-	}
+	public static final String METHOD = "getEdgeConfig";
 
-	public final static String METHOD = "getEdgeConfig";
-
-	public GetEdgeConfigRequest() {
-		this(UUID.randomUUID());
+	/**
+	 * Create {@link GetEdgeConfigRequest} from a template {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link GetEdgeConfigRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
+	public static GetEdgeConfigRequest from(JsonrpcRequest r) throws OpenemsException {
+		return new GetEdgeConfigRequest(r);
 	}
 
-	public GetEdgeConfigRequest(UUID id) {
-		super(id, METHOD);
+	private GetEdgeConfigRequest(JsonrpcRequest request) {
+		super(request, METHOD);
 	}
 
 	@Override
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesDataRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesDataRequest.java
index 08c5f62819b..595491e2b1b 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesDataRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesDataRequest.java
@@ -6,17 +6,15 @@
 import java.time.format.DateTimeFormatter;
 import java.util.Optional;
 import java.util.TreeSet;
-import java.util.UUID;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
-import io.openems.common.exceptions.OpenemsException;
-import io.openems.common.jsonrpc.base.GenericJsonrpcRequest;
 import io.openems.common.jsonrpc.base.JsonrpcRequest;
 import io.openems.common.types.ChannelAddress;
+import io.openems.common.utils.DateUtils;
 import io.openems.common.utils.JsonUtils;
 
 /**
@@ -39,10 +37,16 @@
  */
 public class QueryHistoricTimeseriesDataRequest extends JsonrpcRequest {
 
-	public final static String METHOD = "queryHistoricTimeseriesData";
-
-	private final static DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+	public static final String METHOD = "queryHistoricTimeseriesData";
 
+	/**
+	 * Create {@link QueryHistoricTimeseriesDataRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link QueryHistoricTimeseriesDataRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static QueryHistoricTimeseriesDataRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		int timezoneDiff = JsonUtils.getAsInt(p, "timezone");
@@ -50,7 +54,7 @@ public static QueryHistoricTimeseriesDataRequest from(JsonrpcRequest r) throws O
 		ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(p, "fromDate", timezone);
 		ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(p, "toDate", timezone).plusDays(1);
 		Integer resolution = JsonUtils.getAsOptionalInt(p, "resolution").orElse(null);
-		QueryHistoricTimeseriesDataRequest result = new QueryHistoricTimeseriesDataRequest(r.getId(), fromDate, toDate,
+		QueryHistoricTimeseriesDataRequest result = new QueryHistoricTimeseriesDataRequest(r, fromDate, toDate,
 				resolution);
 		JsonArray channels = JsonUtils.getAsJsonArray(p, "channels");
 		for (JsonElement channel : channels) {
@@ -60,9 +64,7 @@ public static QueryHistoricTimeseriesDataRequest from(JsonrpcRequest r) throws O
 		return result;
 	}
 
-	public static QueryHistoricTimeseriesDataRequest from(JsonObject j) throws OpenemsNamedException {
-		return from(GenericJsonrpcRequest.from(j));
-	}
+	private static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
 
 	private final int timezoneDiff;
 	private final ZonedDateTime fromDate;
@@ -74,15 +76,12 @@ public static QueryHistoricTimeseriesDataRequest from(JsonObject j) throws Opene
 	 */
 	private final Integer resolution;
 
-	public QueryHistoricTimeseriesDataRequest(UUID id, ZonedDateTime fromDate, ZonedDateTime toDate, Integer resolution)
-			throws OpenemsNamedException {
-		super(id, METHOD);
+	private QueryHistoricTimeseriesDataRequest(JsonrpcRequest request, ZonedDateTime fromDate, ZonedDateTime toDate,
+			Integer resolution) throws OpenemsNamedException {
+		super(request, METHOD);
 
+		DateUtils.assertSameTimezone(fromDate, toDate);
 		this.timezoneDiff = ZoneOffset.from(fromDate).getTotalSeconds();
-		if (timezoneDiff != ZoneOffset.from(toDate).getTotalSeconds()) {
-			throw new OpenemsException("FromDate and ToDate need to be in the same timezone!");
-		}
-
 		this.fromDate = fromDate;
 		this.toDate = toDate;
 		this.resolution = resolution;
@@ -90,7 +89,13 @@ public QueryHistoricTimeseriesDataRequest(UUID id, ZonedDateTime fromDate, Zoned
 
 	public QueryHistoricTimeseriesDataRequest(ZonedDateTime fromDate, ZonedDateTime toDate, Integer resolution)
 			throws OpenemsNamedException {
-		this(UUID.randomUUID(), fromDate, toDate, resolution);
+		super(METHOD);
+
+		DateUtils.assertSameTimezone(fromDate, toDate);
+		this.timezoneDiff = ZoneOffset.from(fromDate).getTotalSeconds();
+		this.fromDate = fromDate;
+		this.toDate = toDate;
+		this.resolution = resolution;
 	}
 
 	private void addChannel(ChannelAddress address) {
@@ -112,20 +117,40 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the From-Date.
+	 * 
+	 * @return From-Date
+	 */
 	public ZonedDateTime getFromDate() {
-		return fromDate;
+		return this.fromDate;
 	}
 
+	/**
+	 * Gets the To-Date.
+	 * 
+	 * @return To-Date
+	 */
 	public ZonedDateTime getToDate() {
-		return toDate;
+		return this.toDate;
 	}
 
+	/**
+	 * Gets the {@link ChannelAddress}es.
+	 * 
+	 * @return Set of {@link ChannelAddress}
+	 */
 	public TreeSet getChannels() {
-		return channels;
+		return this.channels;
 	}
 
+	/**
+	 * Gets the requested Resolution in [s].
+	 * 
+	 * @return Resolution
+	 */
 	public Optional getResolution() {
-		return Optional.ofNullable(resolution);
+		return Optional.ofNullable(this.resolution);
 	}
 
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyPerPeriodRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyPerPeriodRequest.java
index 26bc80fde69..a29a704cacb 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyPerPeriodRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyPerPeriodRequest.java
@@ -5,17 +5,15 @@
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.TreeSet;
-import java.util.UUID;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
-import io.openems.common.exceptions.OpenemsException;
-import io.openems.common.jsonrpc.base.GenericJsonrpcRequest;
 import io.openems.common.jsonrpc.base.JsonrpcRequest;
 import io.openems.common.types.ChannelAddress;
+import io.openems.common.utils.DateUtils;
 import io.openems.common.utils.JsonUtils;
 
 /**
@@ -45,10 +43,16 @@
 
 public class QueryHistoricTimeseriesEnergyPerPeriodRequest extends JsonrpcRequest {
 
-	public final static String METHOD = "queryHistoricTimeseriesEnergyPerPeriod";
-
-	private final static DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+	public static final String METHOD = "queryHistoricTimeseriesEnergyPerPeriod";
 
+	/**
+	 * Create {@link QueryHistoricTimeseriesEnergyPerPeriodRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link QueryHistoricTimeseriesEnergyPerPeriodRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static QueryHistoricTimeseriesEnergyPerPeriodRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		int timezoneDiff = JsonUtils.getAsInt(p, "timezone");
@@ -56,7 +60,7 @@ public static QueryHistoricTimeseriesEnergyPerPeriodRequest from(JsonrpcRequest
 		ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(p, "fromDate", timezone);
 		ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(p, "toDate", timezone).plusDays(1);
 		int resolution = JsonUtils.getAsInt(p, "resolution");
-		QueryHistoricTimeseriesEnergyPerPeriodRequest result = new QueryHistoricTimeseriesEnergyPerPeriodRequest(r.getId(),
+		QueryHistoricTimeseriesEnergyPerPeriodRequest result = new QueryHistoricTimeseriesEnergyPerPeriodRequest(r,
 				fromDate, toDate, resolution);
 		JsonArray channels = JsonUtils.getAsJsonArray(p, "channels");
 		for (JsonElement channel : channels) {
@@ -66,9 +70,7 @@ public static QueryHistoricTimeseriesEnergyPerPeriodRequest from(JsonrpcRequest
 		return result;
 	}
 
-	public static QueryHistoricTimeseriesEnergyPerPeriodRequest from(JsonObject j) throws OpenemsNamedException {
-		return from(GenericJsonrpcRequest.from(j));
-	}
+	private static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
 
 	private final int timezoneDiff;
 	private final ZonedDateTime fromDate;
@@ -76,15 +78,12 @@ public static QueryHistoricTimeseriesEnergyPerPeriodRequest from(JsonObject j) t
 	private final TreeSet channels = new TreeSet<>();
 	private final int resolution;
 
-	public QueryHistoricTimeseriesEnergyPerPeriodRequest(UUID id, ZonedDateTime fromDate, ZonedDateTime toDate,
-			int resolution) throws OpenemsNamedException {
-		super(id, METHOD);
+	private QueryHistoricTimeseriesEnergyPerPeriodRequest(JsonrpcRequest request, ZonedDateTime fromDate,
+			ZonedDateTime toDate, int resolution) throws OpenemsNamedException {
+		super(request, METHOD);
 
+		DateUtils.assertSameTimezone(fromDate, toDate);
 		this.timezoneDiff = ZoneOffset.from(fromDate).getTotalSeconds();
-		if (timezoneDiff != ZoneOffset.from(toDate).getTotalSeconds()) {
-			throw new OpenemsException("FromDate and ToDate need to be in the same timezone!");
-		}
-
 		this.fromDate = fromDate;
 		this.toDate = toDate;
 		this.resolution = resolution;
@@ -92,7 +91,13 @@ public QueryHistoricTimeseriesEnergyPerPeriodRequest(UUID id, ZonedDateTime from
 
 	public QueryHistoricTimeseriesEnergyPerPeriodRequest(ZonedDateTime fromDate, ZonedDateTime toDate, int resolution)
 			throws OpenemsNamedException {
-		this(UUID.randomUUID(), fromDate, toDate, resolution);
+		super(METHOD);
+
+		DateUtils.assertSameTimezone(fromDate, toDate);
+		this.timezoneDiff = ZoneOffset.from(fromDate).getTotalSeconds();
+		this.fromDate = fromDate;
+		this.toDate = toDate;
+		this.resolution = resolution;
 	}
 
 	private void addChannel(ChannelAddress address) {
@@ -109,23 +114,43 @@ public JsonObject getParams() {
 				.addProperty("fromDate", FORMAT.format(this.fromDate)) //
 				.addProperty("toDate", FORMAT.format(this.toDate)) //
 				.add("channels", channels) //
-				.addProperty("resolution", resolution) //
+				.addProperty("resolution", this.resolution) //
 				.build();
 	}
 
+	/**
+	 * Gets the From-Date.
+	 * 
+	 * @return From-Date
+	 */
 	public ZonedDateTime getFromDate() {
-		return fromDate;
+		return this.fromDate;
 	}
 
+	/**
+	 * Gets the To-Date.
+	 * 
+	 * @return To-Date
+	 */
 	public ZonedDateTime getToDate() {
-		return toDate;
+		return this.toDate;
 	}
 
+	/**
+	 * Gets the {@link ChannelAddress}es.
+	 * 
+	 * @return Set of {@link ChannelAddress}
+	 */
 	public TreeSet getChannels() {
-		return channels;
+		return this.channels;
 	}
 
+	/**
+	 * Gets the requested Resolution in [s].
+	 * 
+	 * @return Resolution
+	 */
 	public int getResolution() {
-		return resolution;
+		return this.resolution;
 	}
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyRequest.java
index 21d70b6ea80..a5bf7e05ee2 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesEnergyRequest.java
@@ -5,17 +5,15 @@
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.TreeSet;
-import java.util.UUID;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
-import io.openems.common.exceptions.OpenemsException;
-import io.openems.common.jsonrpc.base.GenericJsonrpcRequest;
 import io.openems.common.jsonrpc.base.JsonrpcRequest;
 import io.openems.common.types.ChannelAddress;
+import io.openems.common.utils.DateUtils;
 import io.openems.common.utils.JsonUtils;
 
 /**
@@ -44,18 +42,23 @@
 
 public class QueryHistoricTimeseriesEnergyRequest extends JsonrpcRequest {
 
-	public final static String METHOD = "queryHistoricTimeseriesEnergy";
-
-	private final static DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+	public static final String METHOD = "queryHistoricTimeseriesEnergy";
 
+	/**
+	 * Create {@link AuthenticateWithPasswordRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link AuthenticateWithPasswordRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static QueryHistoricTimeseriesEnergyRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		int timezoneDiff = JsonUtils.getAsInt(p, "timezone");
 		ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1));
 		ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(p, "fromDate", timezone);
 		ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(p, "toDate", timezone).plusDays(1);
-		QueryHistoricTimeseriesEnergyRequest result = new QueryHistoricTimeseriesEnergyRequest(r.getId(), fromDate,
-				toDate);
+		QueryHistoricTimeseriesEnergyRequest result = new QueryHistoricTimeseriesEnergyRequest(r, fromDate, toDate);
 		JsonArray channels = JsonUtils.getAsJsonArray(p, "channels");
 		for (JsonElement channel : channels) {
 			ChannelAddress address = ChannelAddress.fromString(JsonUtils.getAsString(channel));
@@ -64,31 +67,31 @@ public static QueryHistoricTimeseriesEnergyRequest from(JsonrpcRequest r) throws
 		return result;
 	}
 
-	public static QueryHistoricTimeseriesEnergyRequest from(JsonObject j) throws OpenemsNamedException {
-		return from(GenericJsonrpcRequest.from(j));
-	}
+	private static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
 
 	private final int timezoneDiff;
 	private final ZonedDateTime fromDate;
 	private final ZonedDateTime toDate;
 	private final TreeSet channels = new TreeSet<>();
 
-	public QueryHistoricTimeseriesEnergyRequest(UUID id, ZonedDateTime fromDate, ZonedDateTime toDate)
+	private QueryHistoricTimeseriesEnergyRequest(JsonrpcRequest request, ZonedDateTime fromDate, ZonedDateTime toDate)
 			throws OpenemsNamedException {
-		super(id, METHOD);
+		super(request, METHOD);
 
+		DateUtils.assertSameTimezone(fromDate, toDate);
 		this.timezoneDiff = ZoneOffset.from(fromDate).getTotalSeconds();
-		if (timezoneDiff != ZoneOffset.from(toDate).getTotalSeconds()) {
-			throw new OpenemsException("FromDate and ToDate need to be in the same timezone!");
-		}
-
 		this.fromDate = fromDate;
 		this.toDate = toDate;
 	}
 
 	public QueryHistoricTimeseriesEnergyRequest(ZonedDateTime fromDate, ZonedDateTime toDate)
 			throws OpenemsNamedException {
-		this(UUID.randomUUID(), fromDate, toDate);
+		super(METHOD);
+
+		DateUtils.assertSameTimezone(fromDate, toDate);
+		this.timezoneDiff = ZoneOffset.from(fromDate).getTotalSeconds();
+		this.fromDate = fromDate;
+		this.toDate = toDate;
 	}
 
 	private void addChannel(ChannelAddress address) {
@@ -108,15 +111,31 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the From-Date.
+	 * 
+	 * @return From-Date
+	 */
 	public ZonedDateTime getFromDate() {
-		return fromDate;
+		return this.fromDate;
 	}
 
+	/**
+	 * Gets the To-Date.
+	 * 
+	 * @return To-Date
+	 */
 	public ZonedDateTime getToDate() {
-		return toDate;
+		return this.toDate;
 	}
 
+	/**
+	 * Gets the {@link ChannelAddress}es.
+	 * 
+	 * @return Set of {@link ChannelAddress}
+	 */
 	public TreeSet getChannels() {
-		return channels;
+		return this.channels;
 	}
+
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesExportXlxsRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesExportXlxsRequest.java
index 65ac6eaceb2..87f67f64adf 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesExportXlxsRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/QueryHistoricTimeseriesExportXlxsRequest.java
@@ -4,13 +4,12 @@
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.UUID;
 
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
-import io.openems.common.exceptions.OpenemsException;
 import io.openems.common.jsonrpc.base.JsonrpcRequest;
+import io.openems.common.utils.DateUtils;
 import io.openems.common.utils.JsonUtils;
 
 /**
@@ -33,33 +32,38 @@ public class QueryHistoricTimeseriesExportXlxsRequest extends JsonrpcRequest {
 
 	public static final String METHOD = "queryHistoricTimeseriesExportXlxs";
 
-	private static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
-
+	/**
+	 * Create {@link QueryHistoricTimeseriesExportXlxsRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link QueryHistoricTimeseriesExportXlxsRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static QueryHistoricTimeseriesExportXlxsRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		int timezoneDiff = JsonUtils.getAsInt(p, "timezone");
 		ZoneId timezone = ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timezoneDiff * -1));
 		ZonedDateTime fromDate = JsonUtils.getAsZonedDateTime(p, "fromDate", timezone);
 		ZonedDateTime toDate = JsonUtils.getAsZonedDateTime(p, "toDate", timezone).plusDays(1);
-		QueryHistoricTimeseriesExportXlxsRequest result = new QueryHistoricTimeseriesExportXlxsRequest(r.getId(),
-				fromDate, toDate);
+		QueryHistoricTimeseriesExportXlxsRequest result = new QueryHistoricTimeseriesExportXlxsRequest(r, fromDate,
+				toDate);
 		return result;
 
 	}
 
+	private static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+
 	private final int timezoneDiff;
 	private final ZonedDateTime fromDate;
 	private final ZonedDateTime toDate;
 
-	public QueryHistoricTimeseriesExportXlxsRequest(UUID id, ZonedDateTime fromDate, ZonedDateTime toDate)
-			throws OpenemsNamedException {
-		super(id, METHOD);
+	private QueryHistoricTimeseriesExportXlxsRequest(JsonrpcRequest request, ZonedDateTime fromDate,
+			ZonedDateTime toDate) throws OpenemsNamedException {
+		super(request, METHOD);
 
+		DateUtils.assertSameTimezone(fromDate, toDate);
 		this.timezoneDiff = ZoneOffset.from(fromDate).getTotalSeconds();
-		if (this.timezoneDiff != ZoneOffset.from(toDate).getTotalSeconds()) {
-			throw new OpenemsException("FromDate and ToDate need to be in the same timezone!");
-		}
-
 		this.fromDate = fromDate;
 		this.toDate = toDate;
 	}
@@ -73,10 +77,20 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the From-Date.
+	 * 
+	 * @return From-Date
+	 */
 	public ZonedDateTime getFromDate() {
 		return this.fromDate;
 	}
 
+	/**
+	 * Gets the To-Date.
+	 * 
+	 * @return To-Date
+	 */
 	public ZonedDateTime getToDate() {
 		return this.toDate;
 	}
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/SetChannelValueRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/SetChannelValueRequest.java
index a5a22764dd9..c85a4aa382c 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/SetChannelValueRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/SetChannelValueRequest.java
@@ -1,7 +1,5 @@
 package io.openems.common.jsonrpc.request;
 
-import java.util.UUID;
-
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 
@@ -28,26 +26,36 @@
  */
 public class SetChannelValueRequest extends JsonrpcRequest {
 
+	public static final String METHOD = "setChannelValue";
+
+	/**
+	 * Create {@link SetChannelValueRequest} from a template {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link SetChannelValueRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static SetChannelValueRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		String componentId = JsonUtils.getAsString(p, "componentId");
 		String channelId = JsonUtils.getAsString(p, "channelId");
 		JsonElement value = JsonUtils.getSubElement(p, "value");
-		return new SetChannelValueRequest(r.getId(), componentId, channelId, value);
+		return new SetChannelValueRequest(r, componentId, channelId, value);
 	}
 
-	public final static String METHOD = "setChannelValue";
-
 	private final String componentId;
 	private final String channelId;
 	private final JsonElement value;
 
 	public SetChannelValueRequest(String componentId, String channelId, JsonElement value) {
-		this(UUID.randomUUID(), componentId, channelId, value);
+		super(METHOD);
+		this.componentId = componentId;
+		this.channelId = channelId;
+		this.value = value;
 	}
 
-	public SetChannelValueRequest(UUID id, String componentId, String channelId, JsonElement value) {
-		super(id, METHOD);
+	private SetChannelValueRequest(JsonrpcRequest request, String componentId, String channelId, JsonElement value) {
+		super(request, METHOD);
 		this.componentId = componentId;
 		this.channelId = channelId;
 		this.value = value;
@@ -62,19 +70,39 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the Component-ID.
+	 * 
+	 * @return Component-ID
+	 */
 	public String getComponentId() {
-		return componentId;
+		return this.componentId;
 	}
 
+	/**
+	 * Gets the Channel-ID.
+	 * 
+	 * @return Channel-ID
+	 */
 	public String getChannelId() {
-		return channelId;
+		return this.channelId;
 	}
 
+	/**
+	 * Gets the {@link ChannelAddress}.
+	 * 
+	 * @return ChannelAddress
+	 */
 	public ChannelAddress getChannelAddress() {
 		return new ChannelAddress(this.componentId, this.channelId);
 	}
 
+	/**
+	 * Gets the Value.
+	 * 
+	 * @return Value
+	 */
 	public JsonElement getValue() {
-		return value;
+		return this.value;
 	}
 }
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/SetGridConnScheduleRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/SetGridConnScheduleRequest.java
index 8f6bf25079b..a85cc54eaa8 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/SetGridConnScheduleRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/SetGridConnScheduleRequest.java
@@ -3,7 +3,6 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
-import java.util.UUID;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
@@ -34,12 +33,20 @@
  */
 public class SetGridConnScheduleRequest extends JsonrpcRequest {
 
+	/**
+	 * Create {@link SetGridConnScheduleRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link SetGridConnScheduleRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static SetGridConnScheduleRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		String edgeId = JsonUtils.getAsString(p, "id");
 		JsonArray s = JsonUtils.getAsJsonArray(p, "schedule");
 		List schedule = GridConnSchedule.from(s);
-		return new SetGridConnScheduleRequest(r.getId(), edgeId, schedule);
+		return new SetGridConnScheduleRequest(r, edgeId, schedule);
 	}
 
 	public static final String METHOD = "setGridConnSchedule";
@@ -48,19 +55,26 @@ public static SetGridConnScheduleRequest from(JsonrpcRequest r) throws OpenemsNa
 	private final List schedule;
 
 	public SetGridConnScheduleRequest(String edgeId) {
-		this(UUID.randomUUID(), edgeId, new ArrayList<>());
+		this(edgeId, new ArrayList<>());
 	}
 
 	public SetGridConnScheduleRequest(String edgeId, List schedule) {
-		this(UUID.randomUUID(), edgeId, schedule);
+		super(METHOD);
+		this.edgeId = edgeId;
+		this.schedule = schedule;
 	}
 
-	public SetGridConnScheduleRequest(UUID id, String edgeId, List schedule) {
-		super(id, METHOD);
+	private SetGridConnScheduleRequest(JsonrpcRequest request, String edgeId, List schedule) {
+		super(request, METHOD);
 		this.edgeId = edgeId;
 		this.schedule = schedule;
 	}
 
+	/**
+	 * Add a {@link GridConnSchedule} entry.
+	 * 
+	 * @param scheduleEntry GridConnSchedule entry
+	 */
 	public void addScheduleEntry(GridConnSchedule scheduleEntry) {
 		this.schedule.add(scheduleEntry);
 	}
@@ -77,16 +91,33 @@ public JsonObject getParams() {
 				.build();
 	}
 
+	/**
+	 * Gets the Edge-ID.
+	 * 
+	 * @return Edge-ID
+	 */
 	public String getEdgeId() {
 		return this.edgeId;
 	}
 
+	/**
+	 * Gets the list of {@link GridConnSchedule} entries.
+	 * 
+	 * @return entries
+	 */
 	public List getSchedule() {
 		return this.schedule;
 	}
 
 	public static class GridConnSchedule {
 
+		/**
+		 * Create a list of {@link GridConnSchedule}s from a {@link JsonArray}.
+		 * 
+		 * @param j the {@link JsonArray}
+		 * @return the list of {@link GridConnSchedule}s
+		 * @throws OpenemsNamedException on parse error
+		 */
 		public static List from(JsonArray j) throws OpenemsNamedException {
 			List schedule = new ArrayList<>();
 			for (JsonElement se : j) {
@@ -116,19 +147,34 @@ public GridConnSchedule(long startTimestamp, int duration, int activePowerSetPoi
 			this.activePowerSetPoint = activePowerSetPoint;
 		}
 
+		/**
+		 * Gets the start timestamp in epoch seconds.
+		 * 
+		 * @return start timestamp
+		 */
 		public long getStartTimestamp() {
 			return this.startTimestamp;
 		}
 
+		/**
+		 * Gets the duration in seconds.
+		 * 
+		 * @return duration
+		 */
 		public int getDuration() {
 			return this.duration;
 		}
 
+		/**
+		 * Gets the Active-Power Setpoint.
+		 * 
+		 * @return the setpoint
+		 */
 		public int getActivePowerSetPoint() {
 			return this.activePowerSetPoint;
 		}
 
-		public JsonObject toJson() {
+		protected JsonObject toJson() {
 			return JsonUtils.buildJsonObject() //
 					.addProperty("startTimestamp", this.getStartTimestamp()) //
 					.addProperty("duration", this.getDuration()) //
diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeChannelsRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeChannelsRequest.java
index 19f476e3d34..6965ecfceab 100644
--- a/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeChannelsRequest.java
+++ b/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeChannelsRequest.java
@@ -1,14 +1,12 @@
 package io.openems.common.jsonrpc.request;
 
 import java.util.TreeSet;
-import java.util.UUID;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 
 import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
-import io.openems.common.jsonrpc.base.GenericJsonrpcRequest;
 import io.openems.common.jsonrpc.base.JsonrpcRequest;
 import io.openems.common.types.ChannelAddress;
 import io.openems.common.utils.JsonUtils;
@@ -16,6 +14,7 @@
 /**
  * Represents a JSON-RPC Request to subscribe to Channels.
  * 
+ * 

* This is used by UI to get regular updates on specific channels. * *

@@ -32,12 +31,20 @@
  */
 public class SubscribeChannelsRequest extends JsonrpcRequest {
 
-	public final static String METHOD = "subscribeChannels";
+	public static final String METHOD = "subscribeChannels";
 
+	/**
+	 * Create {@link SubscribeChannelsRequest} from a template
+	 * {@link JsonrpcRequest}.
+	 * 
+	 * @param r the template {@link JsonrpcRequest}
+	 * @return the {@link SubscribeChannelsRequest}
+	 * @throws OpenemsNamedException on parse error
+	 */
 	public static SubscribeChannelsRequest from(JsonrpcRequest r) throws OpenemsNamedException {
 		JsonObject p = r.getParams();
 		int count = JsonUtils.getAsInt(p, "count");
-		SubscribeChannelsRequest result = new SubscribeChannelsRequest(r.getId(), count);
+		SubscribeChannelsRequest result = new SubscribeChannelsRequest(r, count);
 		JsonArray channels = JsonUtils.getAsJsonArray(p, "channels");
 		for (JsonElement channel : channels) {
 			ChannelAddress address = ChannelAddress.fromString(JsonUtils.getAsString(channel));
@@ -46,32 +53,42 @@ public static SubscribeChannelsRequest from(JsonrpcRequest r) throws OpenemsName
 		return result;
 	}
 
-	public static SubscribeChannelsRequest from(JsonObject j) throws OpenemsNamedException {
-		return from(GenericJsonrpcRequest.from(j));
-	}
-
 	private final int count;
 	private final TreeSet channels = new TreeSet<>();
 
-	public SubscribeChannelsRequest(UUID id, int count) {
-		super(id, METHOD);
+	private SubscribeChannelsRequest(JsonrpcRequest request, int count) {
+		super(request, METHOD);
 		this.count = count;
 	}
 
 	public SubscribeChannelsRequest(int count) {
-		this(UUID.randomUUID(), count);
+		super(METHOD);
+		this.count = count;
 	}
 
 	private void addChannel(ChannelAddress address) {
 		this.channels.add(address);
 	}
 
+	/**
+	 * Gets the Count value.
+	 * 
+	 * 

+ * This value is increased with every request to assure order. + * + * @return the count value + */ public int getCount() { return this.count; } + /** + * Gets the set of {@link ChannelAddress}es. + * + * @return the {@link ChannelAddress}es + */ public TreeSet getChannels() { - return channels; + return this.channels; } @Override diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeSystemLogRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeSystemLogRequest.java index 727b275c437..acc566f15cb 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeSystemLogRequest.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/request/SubscribeSystemLogRequest.java @@ -1,11 +1,8 @@ package io.openems.common.jsonrpc.request; -import java.util.UUID; - import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.utils.JsonUtils; @@ -32,35 +29,55 @@ public class SubscribeSystemLogRequest extends JsonrpcRequest { public static final String METHOD = "subscribeSystemLog"; + /** + * Create {@link SubscribeSystemLogRequest} from a template + * {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link SubscribeSystemLogRequest} + * @throws OpenemsNamedException on parse error + */ public static SubscribeSystemLogRequest from(JsonrpcRequest r) throws OpenemsNamedException { JsonObject p = r.getParams(); boolean subscribe = JsonUtils.getAsBoolean(p, "subscribe"); - return new SubscribeSystemLogRequest(r.getId(), subscribe); - } - - public static SubscribeSystemLogRequest from(JsonObject j) throws OpenemsNamedException { - return from(GenericJsonrpcRequest.from(j)); + return new SubscribeSystemLogRequest(r, subscribe); } + /** + * Creates a JSON-RPC Request that subscribes the System-Log. + * + * @return {@link SubscribeSystemLogRequest} + */ public static SubscribeSystemLogRequest subscribe() { return new SubscribeSystemLogRequest(true); } + /** + * Creates a JSON-RPC Request that unsubscribes the System-Log. + * + * @return {@link SubscribeSystemLogRequest} + */ public static SubscribeSystemLogRequest unsubscribe() { return new SubscribeSystemLogRequest(false); } private final boolean subscribe; - private SubscribeSystemLogRequest(UUID id, boolean subscribe) { - super(id, METHOD); + private SubscribeSystemLogRequest(JsonrpcRequest request, boolean subscribe) { + super(request, METHOD); this.subscribe = subscribe; } public SubscribeSystemLogRequest(boolean subscribe) { - this(UUID.randomUUID(), subscribe); + super(METHOD); + this.subscribe = subscribe; } + /** + * Whether to subscribe or unsubscribe. + * + * @return true for subscribe, false for unsubscribe + */ public boolean getSubscribe() { return this.subscribe; } diff --git a/io.openems.common/src/io/openems/common/jsonrpc/request/UpdateComponentConfigRequest.java b/io.openems.common/src/io/openems/common/jsonrpc/request/UpdateComponentConfigRequest.java index 614e87bb373..7680bbb16b9 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/request/UpdateComponentConfigRequest.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/request/UpdateComponentConfigRequest.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.UUID; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -33,24 +32,34 @@ */ public class UpdateComponentConfigRequest extends JsonrpcRequest { + public static final String METHOD = "updateComponentConfig"; + + /** + * Create {@link UpdateComponentConfigRequest} from a template + * {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link UpdateComponentConfigRequest} + * @throws OpenemsNamedException on parse error + */ public static UpdateComponentConfigRequest from(JsonrpcRequest r) throws OpenemsNamedException { JsonObject p = r.getParams(); String componentId = JsonUtils.getAsString(p, "componentId"); List properties = Property.from(JsonUtils.getAsJsonArray(p, "properties")); - return new UpdateComponentConfigRequest(r.getId(), componentId, properties); + return new UpdateComponentConfigRequest(r, componentId, properties); } - public final static String METHOD = "updateComponentConfig"; - private final String componentId; private final List properties; public UpdateComponentConfigRequest(String componentId, List properties) { - this(UUID.randomUUID(), componentId, properties); + super(METHOD); + this.componentId = componentId; + this.properties = properties; } - public UpdateComponentConfigRequest(UUID id, String componentId, List properties) { - super(id, METHOD); + private UpdateComponentConfigRequest(JsonrpcRequest request, String componentId, List properties) { + super(request, METHOD); this.componentId = componentId; this.properties = properties; } @@ -67,17 +76,27 @@ public JsonObject getParams() { .build(); } + /** + * Gets the Component-ID. + * + * @return Component-ID + */ public String getComponentId() { - return componentId; + return this.componentId; } + /** + * Gets a list of Properties. + * + * @return the Properties + */ public List getProperties() { return this.properties; } public static class Property { - public static List from(JsonArray j) throws OpenemsNamedException { + protected static List from(JsonArray j) throws OpenemsNamedException { List properties = new ArrayList<>(); for (JsonElement property : j) { String name = JsonUtils.getAsString(property, "name"); @@ -91,6 +110,8 @@ public static List from(JsonArray j) throws OpenemsNamedException { private final JsonElement value; /** + * Initializes a Property. + * * @param name the Property name * @param value the new value */ @@ -112,15 +133,25 @@ public Property(String name, Number value) { this(name, new JsonPrimitive(value)); } + /** + * Gets the Name. + * + * @return Name + */ public String getName() { return this.name; } + /** + * Gets the Value. + * + * @return Value + */ public JsonElement getValue() { return this.value; } - public JsonObject toJson() { + protected JsonObject toJson() { return JsonUtils.buildJsonObject() // .addProperty("name", this.getName()) // .add("value", this.getValue()) // diff --git a/io.openems.common/src/io/openems/common/jsonrpc/response/AuthenticateWithPasswordResponse.java b/io.openems.common/src/io/openems/common/jsonrpc/response/AuthenticateWithPasswordResponse.java index 9f32b634e2f..44d52373a94 100644 --- a/io.openems.common/src/io/openems/common/jsonrpc/response/AuthenticateWithPasswordResponse.java +++ b/io.openems.common/src/io/openems/common/jsonrpc/response/AuthenticateWithPasswordResponse.java @@ -17,7 +17,7 @@ * "jsonrpc": "2.0", * "id": "UUID", * "result": { - * "token": UUID, + * "token": String, * "edges": {@link EdgeMetadata#toJson(java.util.Collection)} * } * } @@ -25,27 +25,27 @@ */ public class AuthenticateWithPasswordResponse extends JsonrpcResponseSuccess { - private final UUID token; + private final String token; private final List metadatas; - public AuthenticateWithPasswordResponse(UUID id, UUID token, List metadatas) { + public AuthenticateWithPasswordResponse(UUID id, String token, List metadatas) { super(id); this.token = token; this.metadatas = metadatas; } - public UUID getToken() { - return token; + public String getToken() { + return this.token; } public List getMetadatas() { - return metadatas; + return this.metadatas; } @Override public JsonObject getResult() { return JsonUtils.buildJsonObject() // - .addProperty("token", this.token.toString()) // + .addProperty("token", this.token) // .add("edges", EdgeMetadata.toJson(this.metadatas)) // .build(); } diff --git a/io.openems.common/src/io/openems/common/utils/DateUtils.java b/io.openems.common/src/io/openems/common/utils/DateUtils.java new file mode 100644 index 00000000000..ed85a2da403 --- /dev/null +++ b/io.openems.common/src/io/openems/common/utils/DateUtils.java @@ -0,0 +1,25 @@ +package io.openems.common.utils; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import io.openems.common.exceptions.OpenemsException; + +public class DateUtils { + + private DateUtils() { + } + + /** + * Asserts that both dates are in the same timezone. + * + * @param date1 the first Date + * @param date1 the second Date + * @throws OpenemsException if dates are not in the same timezone + */ + public static void assertSameTimezone(ZonedDateTime date1, ZonedDateTime date2) throws OpenemsException { + if (ZoneOffset.from(date1).getTotalSeconds() != ZoneOffset.from(date2).getTotalSeconds()) { + throw new OpenemsException("FromDate and ToDate need to be in the same timezone!"); + } + } +} diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocket.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocket.java index ca2f5c649e8..5a9a7820d6e 100644 --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocket.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocket.java @@ -1,13 +1,31 @@ package io.openems.common.websocket; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class AbstractWebsocket { + private final Logger log = LoggerFactory.getLogger(AbstractWebsocket.class); private final String name; + /** + * Shared {@link ExecutorService}. Configuration is equal to + * Executors.newCachedThreadPool(), but with DiscardOldestPolicy. + */ + private final ThreadPoolExecutor executor; + + /* + * This Executor is used if Debug-Mode is activated. + */ + private final ScheduledExecutorService debugLogExecutor; + /** * Creates an empty WsData object that is attached to the WebSocket as early as * possible @@ -58,8 +76,30 @@ public abstract class AbstractWebsocket { */ protected abstract OnClose getOnClose(); - public AbstractWebsocket(String name) { + /** + * Construct this {@link AbstractWebsocket}. + * + * @param name a name that is used to identify log messages + * @param poolSize number of threads dedicated to handle the tasks + * @param debugMode activate a regular debug log about the state of the tasks + */ + public AbstractWebsocket(String name, int poolSize, boolean debugMode) { this.name = name; + this.executor = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + if (debugMode) { + this.debugLogExecutor = Executors.newSingleThreadScheduledExecutor(); + this.debugLogExecutor.scheduleWithFixedDelay(() -> { + this.logInfo(this.log, + String.format("[monitor] Pool: %d, Active: %d, Pending: %d, Completed: %d", + this.executor.getPoolSize(), // + this.executor.getActiveCount(), // + this.executor.getQueue().size(), // + this.executor.getCompletedTaskCount())); // + }, 10, 10, TimeUnit.SECONDS); + } else { + this.debugLogExecutor = null; + } } /** @@ -71,13 +111,46 @@ public String getName() { return name; } + protected void start() { + + } + + public void stop() { + // Shutdown executor + if (this.executor != null) { + try { + this.executor.shutdown(); + this.executor.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + this.logWarn(this.log, "tasks interrupted"); + } finally { + if (!this.executor.isTerminated()) { + this.logWarn(this.log, "cancel non-finished tasks"); + } + this.executor.shutdownNow(); + } + } + if (this.debugLogExecutor != null) { + this.debugLogExecutor.shutdown(); + } + } + + /** + * Execute a {@link Runnable} using the shared {@link ExecutorService}. + * + * @param command the {@link Runnable} + */ + protected void execute(Runnable command) { + this.executor.execute(command); + } + /** * Handles an internal Error asynchronously * * @param e */ protected void handleInternalErrorAsync(Exception e) { - CompletableFuture.runAsync(new OnInternalErrorHandler(this.getOnInternalError(), e)); + this.execute(new OnInternalErrorHandler(this.getOnInternalError(), e)); } /** diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java index 7837ccaeccd..fd68be541e8 100644 --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java @@ -38,6 +38,8 @@ public abstract class AbstractWebsocketClient extends Abstract public final static Proxy NO_PROXY = null; public final static Draft DEFAULT_DRAFT = new Draft_6455(); + private final static int MAXIMUM_POOL_SIZE = 10; + protected final WebSocketClient ws; private final Logger log = LoggerFactory.getLogger(AbstractWebsocketClient.class); @@ -58,14 +60,14 @@ protected AbstractWebsocketClient(String name, URI serverUri, Map httpHeaders, Proxy proxy) { - super(name); + super(name, MAXIMUM_POOL_SIZE, false /* debugMode */); this.serverUri = serverUri; this.ws = new WebSocketClient(serverUri, draft, httpHeaders) { @Override public void onOpen(ServerHandshake handshake) { JsonObject jHandshake = WebsocketUtils.handshakeToJsonObject(handshake); - CompletableFuture.runAsync( + AbstractWebsocketClient.this.execute( new OnOpenHandler(AbstractWebsocketClient.this, AbstractWebsocketClient.this.ws, jHandshake)); } @@ -74,17 +76,17 @@ public void onMessage(String stringMessage) { try { JsonrpcMessage message = JsonrpcMessage.from(stringMessage); if (message instanceof JsonrpcRequest) { - CompletableFuture.runAsync(new OnRequestHandler(AbstractWebsocketClient.this, ws, + AbstractWebsocketClient.this.execute(new OnRequestHandler(AbstractWebsocketClient.this, ws, (JsonrpcRequest) message, (response) -> { AbstractWebsocketClient.this.sendMessage(response); })); } else if (message instanceof JsonrpcResponse) { - CompletableFuture.runAsync( + AbstractWebsocketClient.this.execute( new OnResponseHandler(AbstractWebsocketClient.this, ws, (JsonrpcResponse) message)); } else if (message instanceof JsonrpcNotification) { - CompletableFuture.runAsync(new OnNotificationHandler(AbstractWebsocketClient.this, ws, + AbstractWebsocketClient.this.execute(new OnNotificationHandler(AbstractWebsocketClient.this, ws, (JsonrpcNotification) message)); } @@ -95,12 +97,13 @@ public void onMessage(String stringMessage) { @Override public void onError(Exception ex) { - CompletableFuture.runAsync(new OnErrorHandler(AbstractWebsocketClient.this, ws, ex)); + AbstractWebsocketClient.this.execute(new OnErrorHandler(AbstractWebsocketClient.this, ws, ex)); } @Override public void onClose(int code, String reason, boolean remote) { - CompletableFuture.runAsync(new OnCloseHandler(AbstractWebsocketClient.this, ws, code, reason, remote)); + AbstractWebsocketClient.this + .execute(new OnCloseHandler(AbstractWebsocketClient.this, ws, code, reason, remote)); AbstractWebsocketClient.this.log.info( "Websocket [" + serverUri.toString() + "] closed. Code [" + code + "] Reason [" + reason + "]"); @@ -118,7 +121,6 @@ public void onClose(int code, String reason, boolean remote) { // Initialize reconnector this.reconnectorWorker = new ClientReconnectorWorker(this); - this.reconnectorWorker.activate(this.getName()); if (proxy != null) { this.ws.setProxy(proxy); @@ -131,6 +133,7 @@ public void onClose(int code, String reason, boolean remote) { public void start() { this.log.info("Opening connection [" + this.getName() + "] to websocket server [" + this.serverUri + "]"); this.ws.connect(); + this.reconnectorWorker.activate(this.getName()); } /** diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java index 0c2ca1357cc..738c0eae000 100644 --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java @@ -4,7 +4,6 @@ import java.net.BindException; import java.net.InetSocketAddress; import java.util.Collection; -import java.util.concurrent.CompletableFuture; import org.java_websocket.WebSocket; import org.java_websocket.handshake.ClientHandshake; @@ -28,11 +27,13 @@ public abstract class AbstractWebsocketServer extends Abstract private final WebSocketServer ws; /** - * @param name to identify this server - * @param port to listen on + * @param name to identify this server + * @param port to listen on + * @param poolSize number of threads dedicated to handle the tasks + * @param debugMode activate a regular debug log about the state of the tasks */ - protected AbstractWebsocketServer(String name, int port) { - super(name); + protected AbstractWebsocketServer(String name, int port, int poolSize, boolean debugMode) { + super(name, poolSize, debugMode); this.port = port; this.ws = new WebSocketServer(new InetSocketAddress(port)) { @@ -46,7 +47,7 @@ public void onOpen(WebSocket ws, ClientHandshake handshake) { wsData.setWebsocket(ws); ws.setAttachment(wsData); JsonObject jHandshake = WebsocketUtils.handshakeToJsonObject(handshake); - CompletableFuture.runAsync(new OnOpenHandler(AbstractWebsocketServer.this, ws, jHandshake)); + AbstractWebsocketServer.this.execute(new OnOpenHandler(AbstractWebsocketServer.this, ws, jHandshake)); } @Override @@ -62,17 +63,17 @@ public void onMessage(WebSocket ws, String stringMessage) { } if (message instanceof JsonrpcRequest) { - CompletableFuture.runAsync(new OnRequestHandler(AbstractWebsocketServer.this, ws, + AbstractWebsocketServer.this.execute(new OnRequestHandler(AbstractWebsocketServer.this, ws, (JsonrpcRequest) message, (response) -> { AbstractWebsocketServer.this.sendMessage(ws, response); })); } else if (message instanceof JsonrpcResponse) { - CompletableFuture.runAsync( + AbstractWebsocketServer.this.execute( new OnResponseHandler(AbstractWebsocketServer.this, ws, (JsonrpcResponse) message)); } else if (message instanceof JsonrpcNotification) { - CompletableFuture.runAsync(new OnNotificationHandler(AbstractWebsocketServer.this, ws, + AbstractWebsocketServer.this.execute(new OnNotificationHandler(AbstractWebsocketServer.this, ws, (JsonrpcNotification) message)); } @@ -86,13 +87,14 @@ public void onError(WebSocket ws, Exception ex) { if (ws == null) { AbstractWebsocketServer.this.handleInternalErrorAsync(ex); } else { - CompletableFuture.runAsync(new OnErrorHandler(AbstractWebsocketServer.this, ws, ex)); + AbstractWebsocketServer.this.execute(new OnErrorHandler(AbstractWebsocketServer.this, ws, ex)); } } @Override public void onClose(WebSocket ws, int code, String reason, boolean remote) { - CompletableFuture.runAsync(new OnCloseHandler(AbstractWebsocketServer.this, ws, code, reason, remote)); + AbstractWebsocketServer.this + .execute(new OnCloseHandler(AbstractWebsocketServer.this, ws, code, reason, remote)); } }; // Allow the port to be reused. See @@ -136,10 +138,21 @@ public void broadcastMessage(JsonrpcMessage message) { } } + /** + * Gets the port number that this server listens on. + * + * @return The port number. + */ + public int getPort() { + return this.ws.getPort(); + } + /** * Starts the websocket server */ + @Override public void start() { + super.start(); this.log.info("Starting [" + this.getName() + "] websocket server [port=" + this.port + "]"); this.ws.start(); } @@ -147,6 +160,7 @@ public void start() { /** * Stops the websocket server */ + @Override public void stop() { int tries = 3; while (tries-- > 0) { @@ -164,6 +178,7 @@ public void stop() { } } this.log.error("Stopping websocket server [" + this.getName() + "] failed too often."); + super.stop(); } /** diff --git a/io.openems.common/src/io/openems/common/websocket/ClientReconnectorWorker.java b/io.openems.common/src/io/openems/common/websocket/ClientReconnectorWorker.java index f87f6202ff7..55c68f4b806 100644 --- a/io.openems.common/src/io/openems/common/websocket/ClientReconnectorWorker.java +++ b/io.openems.common/src/io/openems/common/websocket/ClientReconnectorWorker.java @@ -1,7 +1,7 @@ package io.openems.common.websocket; import java.time.Duration; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Random; import org.java_websocket.client.WebSocketClient; @@ -13,15 +13,15 @@ public class ClientReconnectorWorker extends AbstractWorker { - private final static int MAX_WAIT_TIME_SECONDS = 120; - private final static int MIN_WAIT_TIME_SECONDS = 10; + private final static int MAX_WAIT_SECONDS = 120; + private final static int MIN_WAIT_SECONDS = 10; - private final static Duration MIN_WAIT_TIME_BETWEEN_RETRIES = Duration - .ofSeconds(new Random().nextInt(MAX_WAIT_TIME_SECONDS) + MIN_WAIT_TIME_SECONDS); + private final static long MIN_WAIT_SEONDCS_BETWEEN_RETRIES = new Random().nextInt(MAX_WAIT_SECONDS) + + MIN_WAIT_SECONDS; private final Logger log = LoggerFactory.getLogger(ClientReconnectorWorker.class); private final AbstractWebsocketClient parent; - private LocalDateTime lastTry = LocalDateTime.MIN; + private Instant lastTry = null;; public ClientReconnectorWorker(AbstractWebsocketClient parent) { this.parent = parent; @@ -38,14 +38,20 @@ protected void forever() throws InterruptedException { return; } - Duration notWaitedEnough = Duration.between(LocalDateTime.now().minus(MIN_WAIT_TIME_BETWEEN_RETRIES), - this.lastTry); - if (!notWaitedEnough.isNegative()) { - this.parent.logInfo(this.log, - "Waiting till next WebSocket reconnect [" + notWaitedEnough.getSeconds() + "s]"); + Instant now = Instant.now(); + + if (this.lastTry == null) { + this.lastTry = now; + return; + } + + long waitedSeconds = Duration.between(this.lastTry, now).getSeconds(); + if (waitedSeconds < MIN_WAIT_SEONDCS_BETWEEN_RETRIES) { + this.parent.logInfo(this.log, "Waiting till next WebSocket reconnect [" + + (MIN_WAIT_SEONDCS_BETWEEN_RETRIES - waitedSeconds) + "s]"); return; } - this.lastTry = LocalDateTime.now(); + this.lastTry = now; this.parent.logInfo(this.log, "Reconnecting WebSocket..."); ws.reconnectBlocking(); diff --git a/io.openems.common/src/io/openems/common/websocket/DummyWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/DummyWebsocketServer.java new file mode 100644 index 00000000000..bd9f7945cd0 --- /dev/null +++ b/io.openems.common/src/io/openems/common/websocket/DummyWebsocketServer.java @@ -0,0 +1,163 @@ +package io.openems.common.websocket; + +import org.java_websocket.server.WebSocketServer; +import org.slf4j.Logger; + +import io.openems.common.exceptions.NotImplementedException; + +public class DummyWebsocketServer extends AbstractWebsocketServer implements AutoCloseable { + + private static class DummyWsData extends WsData { + + @Override + public String toString() { + return "DummyWsData[]"; + } + + } + + public static class Builder { + private OnOpen onOpen = (ws, handshake) -> { + }; + private OnRequest onRequest = (ws, request) -> { + throw new NotImplementedException("On-Request handler is not implemented"); + }; + private OnNotification onNotification = (ws, notification) -> { + }; + private OnError onError = (ws, ex) -> { + }; + private OnClose onClose = (ws, code, reason, remote) -> { + }; + + private Builder() { + } + + public DummyWebsocketServer.Builder onOpen(OnOpen onOpen) { + this.onOpen = onOpen; + return this; + } + + public DummyWebsocketServer.Builder onRequest(OnRequest onRequest) { + this.onRequest = onRequest; + return this; + } + + public DummyWebsocketServer.Builder onNotification(OnNotification onNotification) { + this.onNotification = onNotification; + return this; + } + + public DummyWebsocketServer.Builder onError(OnError onError) { + this.onError = onError; + return this; + } + + public DummyWebsocketServer.Builder onClose(OnClose onClose) { + this.onClose = onClose; + return this; + } + + public DummyWebsocketServer build() { + return new DummyWebsocketServer(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static DummyWebsocketServer.Builder create() { + return new Builder(); + } + + private final DummyWebsocketServer.Builder builder; + + private DummyWebsocketServer(DummyWebsocketServer.Builder builder) { + super("DummyWebsocketServer", 0 /* auto-select port */, 1 /* pool size */, false); + this.builder = builder; + } + + @Override + protected WsData createWsData() { + return new DummyWsData(); + } + + @Override + protected OnOpen getOnOpen() { + return this.builder.onOpen; + } + + public void withOnOpen(OnOpen onOpen) { + this.builder.onOpen = onOpen; + } + + @Override + protected OnRequest getOnRequest() { + return this.builder.onRequest; + } + + public void withOnRequest(OnRequest onRequest) { + this.builder.onRequest = onRequest; + } + + @Override + protected OnNotification getOnNotification() { + return this.builder.onNotification; + } + + public void withOnNotification(OnNotification onNotification) { + this.builder.onNotification = onNotification; + } + + @Override + protected OnError getOnError() { + return this.builder.onError; + } + + public void withOnError(OnError onError) { + this.builder.onError = onError; + } + + @Override + protected OnClose getOnClose() { + return this.builder.onClose; + } + + public void withOnClose(OnClose onClose) { + this.builder.onClose = onClose; + } + + @Override + protected void logInfo(Logger log, String message) { + log.info(message); + } + + @Override + protected void logWarn(Logger log, String message) { + log.info(message); + } + + /** + * Starts the {@link WebSocketServer} and waits. + * + * @return the dynamically assigned Port. + * @throws InterruptedException on error + */ + public int startBlocking() throws InterruptedException { + this.start(); + + // block until Port is not anymore zero + int port; + do { + Thread.sleep(500); + port = this.getPort(); + } while (port == 0); + return port; + } + + @Override + public void close() throws Exception { + this.stop(); + } +} diff --git a/io.openems.common/src/io/openems/common/websocket/OnRequestHandler.java b/io.openems.common/src/io/openems/common/websocket/OnRequestHandler.java index 3a2ddc4e494..24fcf65b37d 100644 --- a/io.openems.common/src/io/openems/common/websocket/OnRequestHandler.java +++ b/io.openems.common/src/io/openems/common/websocket/OnRequestHandler.java @@ -2,6 +2,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import org.java_websocket.WebSocket; @@ -38,14 +40,22 @@ public final void run() { CompletableFuture responseFuture = this.parent.getOnRequest().run(this.ws, this.request); // Get success response - response = responseFuture.get(); + if (this.request.getTimeout().isPresent() && this.request.getTimeout().get() > 0) { + // ...with timeout + response = responseFuture.get(this.request.getTimeout().get(), TimeUnit.SECONDS); + } else { + // ...without timeout + response = responseFuture.get(); + } } catch (OpenemsNamedException e) { // Get Named Exception error response this.parent.logWarn(this.log, "JSON-RPC Error Response: " + e.getMessage()); response = new JsonrpcResponseError(request.getId(), e); - } catch (ExecutionException | InterruptedException e) { + } catch (ExecutionException | InterruptedException | TimeoutException e) { // Get GENERIC error response - this.parent.logWarn(this.log, "JSON-RPC Error Response: " + e.getMessage()); + this.parent.logWarn(this.log, "JSON-RPC Error Response. " + e.getClass().getSimpleName() + ". " // + + "Request: " + this.request.toString() + ". " // + + "Message: " + e.getMessage()); response = new JsonrpcResponseError(request.getId(), e.getMessage()); } diff --git a/io.openems.common/test/io/openems/common/jsonrpc/base/GenericJsonrpcRequestTest.java b/io.openems.common/test/io/openems/common/jsonrpc/base/GenericJsonrpcRequestTest.java new file mode 100644 index 00000000000..31111f054cc --- /dev/null +++ b/io.openems.common/test/io/openems/common/jsonrpc/base/GenericJsonrpcRequestTest.java @@ -0,0 +1,45 @@ +package io.openems.common.jsonrpc.base; + +import static org.junit.Assert.assertEquals; + +import java.util.Optional; +import java.util.UUID; + +import org.junit.Test; + +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.utils.JsonUtils; + +public class GenericJsonrpcRequestTest { + + @Test + public void test() throws OpenemsNamedException { + UUID id = UUID.randomUUID(); + String method = "dummyMethod"; + JsonObject params = JsonUtils.buildJsonObject() // + .addProperty("hello", "world") // + .addProperty("foo", "bar") // + .build(); + int timeout = 10; + + // Test toString() + GenericJsonrpcRequest sut = new GenericJsonrpcRequest(id, method, params, timeout); + String json = sut.toString(); + + assertEquals( + "{\"jsonrpc\":\"2.0\",\"method\":\"dummyMethod\",\"params\":{\"hello\":\"world\",\"foo\":\"bar\"},\"id\":\"" + + id.toString() + "\",\"timeout\":10}", + json); + + // Test from() + sut = GenericJsonrpcRequest.from(json); + assertEquals(id, sut.getId()); + assertEquals(method, sut.getMethod()); + assertEquals(Optional.of(timeout), sut.getTimeout()); + assertEquals("{\"hello\":\"world\",\"foo\":\"bar\"}", sut.getParams().toString()); + + } + +} diff --git a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java b/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java index c5d379cc6ec..7a406830559 100644 --- a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java +++ b/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Battery.java @@ -3,12 +3,11 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.channel.AccessMode; -import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.IntegerReadChannel; -import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; @@ -34,6 +33,7 @@ public interface Battery extends StartStoppable, OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + /** * State of Charge. * @@ -45,7 +45,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ SOC(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT)), + .unit(Unit.PERCENT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * State of Health. @@ -58,7 +59,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ SOH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT)), + .unit(Unit.PERCENT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Voltage of battery. @@ -70,7 +72,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT)), + .unit(Unit.VOLT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Current of battery. @@ -82,7 +85,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Capacity of battery. @@ -94,7 +98,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CAPACITY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Maximal voltage for charging. @@ -106,7 +111,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CHARGE_MAX_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT)), + .unit(Unit.VOLT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Maximum current for charging. @@ -119,7 +125,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CHARGE_MAX_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Minimal voltage for discharging. @@ -131,7 +138,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DISCHARGE_MIN_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT)), + .unit(Unit.VOLT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Maximum current for discharging. @@ -144,7 +152,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DISCHARGE_MAX_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Minimal Cell Temperature. @@ -156,7 +165,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ MIN_CELL_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)), + .unit(Unit.DEGREE_CELSIUS) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Maximum Cell Temperature. @@ -169,7 +179,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ MAX_CELL_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)), + .unit(Unit.DEGREE_CELSIUS) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Minimal cell voltage. @@ -193,31 +204,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ MAX_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT)), - - // TODO FORCE_CHARGE_ACTIVE and FORCE_DISCHARGE_ACTIVE channels are - // deprecated/obsolete by BatteryProtection channels - /** - * Force charge active. - * - *

    - *
  • Interface: Battery - *
  • Indicates that battery is in force charge mode - *
- */ - FORCE_CHARGE_ACTIVE(Doc.of(Level.INFO).text("Force charge mode is active")), // - - /** - * Force discharge active. - * - *
    - *
  • Interface: Battery - *
  • Indicates that battery is in force discharge mode - *
- */ - FORCE_DISCHARGE_ACTIVE(Doc.of(Level.INFO).text("Force discharge mode is active")), // - - ; + .unit(Unit.MILLIVOLT) // + .persistencePriority(PersistencePriority.HIGH)); private final Doc doc; @@ -744,81 +732,4 @@ public default void _setMaxCellVoltage(Integer value) { public default void _setMaxCellVoltage(int value) { this.getMaxCellVoltageChannel().setNextValue(value); } - - /** - * Gets the Channel for {@link ChannelId#FORCE_CHARGE_ACTIVE}. - * - * @return the Channel - */ - public default StateChannel getForceChargeActiveChannel() { - return this.channel(ChannelId.FORCE_CHARGE_ACTIVE); - } - - /** - * Gets the State. See {@link ChannelId#FORCE_CHARGE_ACTIVE}. - * - * @return the Channel {@link Value} - */ - public default Value getForceChargeActive() { - return this.getForceChargeActiveChannel().value(); - } - - /** - * Internal method to set the 'nextValue' on - * {@link ChannelId#FORCE_CHARGE_ACTIVE} Channel. - * - * @param value the next value - */ - public default void _setForceChargeActive(Boolean value) { - this.getForceChargeActiveChannel().setNextValue(value); - } - - /** - * Internal method to set the 'nextValue' on - * {@link ChannelId#FORCE_CHARGE_ACTIVE} Channel. - * - * @param value the next value - */ - public default void _setForceChargeActive(boolean value) { - this.getForceChargeActiveChannel().setNextValue(value); - } - - /** - * Gets the Channel for {@link ChannelId#FORCE_DISCHARGE_ACTIVE}. - * - * @return the Channel - */ - public default StateChannel getForceDischargeActiveChannel() { - return this.channel(ChannelId.FORCE_DISCHARGE_ACTIVE); - } - - /** - * Gets the State. See {@link ChannelId#FORCE_DISCHARGE_ACTIVE}. - * - * @return the Channel {@link Value} - */ - public default Value getForceDischargeActive() { - return this.getForceDischargeActiveChannel().value(); - } - - /** - * Internal method to set the 'nextValue' on - * {@link ChannelId#FORCE_DISCHARGE_ACTIVE} Channel. - * - * @param value the next value - */ - public default void _setForceDischargeActive(Boolean value) { - this.getForceDischargeActiveChannel().setNextValue(value); - } - - /** - * Internal method to set the 'nextValue' on - * {@link ChannelId#FORCE_DISCHARGE_ACTIVE} Channel. - * - * @param value the next value - */ - public default void _setForceDischargeActive(boolean value) { - this.getForceDischargeActiveChannel().setNextValue(value); - } - } diff --git a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/CellCharacteristic.java b/io.openems.edge.battery.api/src/io/openems/edge/battery/api/CellCharacteristic.java deleted file mode 100644 index 5829decbaaf..00000000000 --- a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/CellCharacteristic.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.openems.edge.battery.api; - -public interface CellCharacteristic { - - /** - * German "Beladeschlussspannung". - * - * @return the final cell charge voltage in [mV] - */ - int getFinalCellChargeVoltage_mV(); - - /** - * German "Entladeschlussspannung". - * - * @return the final cell discharge voltage in [mV] - */ - int getFinalCellDischargeVoltage_mV(); - - /** - * German "Zwangsbeladespannung". - * - * @return the force charge cell voltage in [mV] - */ - int getForceChargeCellVoltage_mV(); - - /** - * German "Zwangsentladespannung". - * - * @return the force discharge cell voltage in [mV] - */ - int getForceDischargeCellVoltage_mV(); - -} diff --git a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/SetAllowedCurrents.java b/io.openems.edge.battery.api/src/io/openems/edge/battery/api/SetAllowedCurrents.java deleted file mode 100644 index b4e0de68c53..00000000000 --- a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/SetAllowedCurrents.java +++ /dev/null @@ -1,192 +0,0 @@ -package io.openems.edge.battery.api; - -import io.openems.edge.common.channel.IntegerReadChannel; - -public class SetAllowedCurrents { - - private int lastMaxChargeCurrentFromBmsMilliAmpere = 0; - private int lastMaxDischargeCurrentFromBmsMilliAmpere = 0; - - private Battery battery; - private CellCharacteristic cellCharacteristic; - private Settings settings; - private IntegerReadChannel maxChargeCurrentChannel; - private IntegerReadChannel maxDischargeCurrentChannel; - - public SetAllowedCurrents(// - Battery battery, // - CellCharacteristic cellCharacteristic, // - Settings settings, // - IntegerReadChannel maxChargeCurrentChannel, // - IntegerReadChannel maxDischargeCurrentChannel // - ) { - super(); - this.battery = battery; - this.cellCharacteristic = cellCharacteristic; - this.settings = settings; - this.maxChargeCurrentChannel = maxChargeCurrentChannel; - this.maxDischargeCurrentChannel = maxDischargeCurrentChannel; - } - - /** - * Calculates the allowed currents. - */ - public void act() { - - int maxChargeCurrentFromBmsMilliAmpere = this.maxChargeCurrentChannel.value().orElse(0); - - // limit increasing - if (maxChargeCurrentFromBmsMilliAmpere > this.lastMaxChargeCurrentFromBmsMilliAmpere + this.settings.getMaxIncreaseMilliAmpere()) { - maxChargeCurrentFromBmsMilliAmpere = this.lastMaxChargeCurrentFromBmsMilliAmpere + this.settings.getMaxIncreaseMilliAmpere(); - } - this.lastMaxChargeCurrentFromBmsMilliAmpere = maxChargeCurrentFromBmsMilliAmpere; - - int maxDischargeCurrentFromBmsMilliAmpere = this.maxDischargeCurrentChannel.value().orElse(0); - - // limit increasing - if (maxDischargeCurrentFromBmsMilliAmpere > this.lastMaxDischargeCurrentFromBmsMilliAmpere + this.settings.getMaxIncreaseMilliAmpere()) { - maxDischargeCurrentFromBmsMilliAmpere = this.lastMaxDischargeCurrentFromBmsMilliAmpere + this.settings.getMaxIncreaseMilliAmpere(); - } - this.lastMaxDischargeCurrentFromBmsMilliAmpere = maxDischargeCurrentFromBmsMilliAmpere; - - setMaxAllowedCurrents(this.battery, this.cellCharacteristic, this.settings, maxChargeCurrentFromBmsMilliAmpere / 1000, - maxDischargeCurrentFromBmsMilliAmpere / 1000); - - } - - /** - * Sets the MaxAllowedCurrents. - * @param battery the {@link Battery} - * @param cellCharacteristic the {@link CellCharacteristic} - * @param settings the {@link Settings} - * @param maxChargeCurrentFromBms int - * @param maxDischargeCurrentFromBms int - */ - public static void setMaxAllowedCurrents(// - Battery battery, // - CellCharacteristic cellCharacteristic, // - Settings settings, // - int maxChargeCurrentFromBms, // - int maxDischargeCurrentFromBms // - ) { - - int maxChargeCurrent = maxChargeCurrentFromBms; - int maxDischargeCurrent = maxDischargeCurrentFromBms; - - if (!areApiValuesPresent(battery)) { - maxChargeCurrent = 0; - maxDischargeCurrent = 0; - } else { - if (isChargingAlready(battery)) { - if (isFurtherChargingNecessary(battery, cellCharacteristic,settings)) { - maxDischargeCurrent = calculateForceDischargeCurrent(battery, settings); - } - } else { - if (isVoltageBelowFinalDischargingVoltage(cellCharacteristic, battery)) { - if (isVoltageHigherThanForceChargeVoltage(cellCharacteristic, battery)) { - maxDischargeCurrent = 0; - } else { - maxDischargeCurrent = calculateForceDischargeCurrent(battery, settings); - } - } - } - - if (isDischargingAlready(battery)) { - if (isFurtherDischargingNecessary(cellCharacteristic, battery)) { - maxChargeCurrent = calculateForceChargeCurrent(battery, settings); - } - } else { - if (isVoltageAboveFinalChargingVoltage(cellCharacteristic, battery)) { - if (isVoltageLowerThanForceDischargeVoltage(cellCharacteristic, battery)) { - maxChargeCurrent = 0; - } else { - maxChargeCurrent = calculateForceChargeCurrent(battery, settings); - } - } - } - } - - setChannelsForCharge(maxChargeCurrent, battery); - setChannelsForDischarge(maxDischargeCurrent, battery); - } - - protected static void setChannelsForDischarge(int maxDischargeCurrent, Battery battery) { - battery._setDischargeMaxCurrent(maxDischargeCurrent); - - boolean forceChargeNecessary = maxDischargeCurrent < 0; - battery._setForceChargeActive(forceChargeNecessary); - } - - protected static void setChannelsForCharge(int maxChargeCurrent, Battery battery) { - battery._setChargeMaxCurrent(maxChargeCurrent); - - boolean forceDischargeNecessary = maxChargeCurrent < 0; - battery._setForceDischargeActive(forceDischargeNecessary); - } - - protected static boolean isVoltageLowerThanForceDischargeVoltage(CellCharacteristic cellCharacteristic, - Battery battery) { - return battery.getMaxCellVoltage().get() < cellCharacteristic.getForceDischargeCellVoltage_mV(); - } - - protected static boolean isVoltageAboveFinalChargingVoltage(CellCharacteristic cellCharacteristic, - Battery battery) { - return battery.getMaxCellVoltage().get() > cellCharacteristic.getFinalCellChargeVoltage_mV(); - } - - protected static boolean isVoltageHigherThanForceChargeVoltage(CellCharacteristic cellCharacteristic, - Battery battery) { - return battery.getMinCellVoltage().get() > cellCharacteristic.getForceChargeCellVoltage_mV(); - } - - protected static boolean isVoltageBelowFinalDischargingVoltage(CellCharacteristic cellCharacteristic, - Battery battery) { - return battery.getMinCellVoltage().get() < cellCharacteristic.getFinalCellDischargeVoltage_mV(); - } - - protected static boolean isFurtherDischargingNecessary(CellCharacteristic cellCharacteristic, Battery battery) { - if (!battery.getForceDischargeActive().isDefined()) { - return false; - } - return (battery.getForceDischargeActive().get() - && battery.getMaxCellVoltage().get() > cellCharacteristic.getFinalCellChargeVoltage_mV()); - } - - protected static boolean isDischargingAlready(Battery battery) { - return (battery.getForceDischargeActive().isDefined() && battery.getForceDischargeActive().get()); - } - - protected static int calculateForceDischargeCurrent(Battery battery, Settings settings) { - return calculateForceCurrent(battery, settings); - } - - protected static int calculateForceChargeCurrent(Battery battery, Settings settings) { - return calculateForceCurrent(battery, settings); - } - - protected static int calculateForceCurrent(Battery battery, Settings settings) { - double capacity = battery.getCapacity().get(); - double voltage = battery.getVoltage().get(); - double power = capacity * settings.getPowerFactor();//POWER_FACTOR; - double current = power / voltage; - int value = -(int) Math.max(settings.getMinimumCurrentAmpere(), current);//MINIMUM_CURRENT_AMPERE, current); - return value; - } - - protected static boolean isFurtherChargingNecessary(Battery battery, CellCharacteristic cellCharacteristic, Settings settings) { - if (!battery.getForceChargeActive().isDefined()) { - return false; - } - return battery.getForceChargeActive().get() - && battery.getMinCellVoltage().get() < (cellCharacteristic.getFinalCellDischargeVoltage_mV() - settings.getToleranceMilliVolt()); - } - - protected static boolean isChargingAlready(Battery battery) { - return (battery.getForceChargeActive().isDefined() && battery.getForceChargeActive().get()); - } - - protected static boolean areApiValuesPresent(Battery battery) { - return battery.getCapacity().isDefined() && battery.getVoltage().isDefined() - && battery.getMinCellVoltage().isDefined() && battery.getMaxCellVoltage().isDefined(); - } -} diff --git a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Settings.java b/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Settings.java deleted file mode 100644 index 001eab1681a..00000000000 --- a/io.openems.edge.battery.api/src/io/openems/edge/battery/api/Settings.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.openems.edge.battery.api; - -public interface Settings { - - /** - * Gets the max increase in milli ampere. - * @return int - */ - int getMaxIncreaseMilliAmpere(); - - /** - * Gets the power factor. - * @return double - */ - double getPowerFactor(); - - /** - * Gets the minimal current in ampere. - * @return double - */ - double getMinimumCurrentAmpere(); - - /** - * Gets the tolerance in millivolt. - * @return int - */ - int getToleranceMilliVolt(); - -} \ No newline at end of file diff --git a/io.openems.edge.battery.api/src/io/openems/edge/battery/protection/BatteryProtection.java b/io.openems.edge.battery.api/src/io/openems/edge/battery/protection/BatteryProtection.java index b87af843273..5689735a788 100644 --- a/io.openems.edge.battery.api/src/io/openems/edge/battery/protection/BatteryProtection.java +++ b/io.openems.edge.battery.api/src/io/openems/edge/battery/protection/BatteryProtection.java @@ -1,5 +1,6 @@ package io.openems.edge.battery.protection; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.battery.api.Battery; @@ -39,7 +40,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_CHARGE_BMS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Discharge Current limit provided by the Battery/BMS. * @@ -50,7 +52,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_DISCHARGE_BMS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Charge Current limit derived from Min-Cell-Voltage. * @@ -61,7 +64,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_CHARGE_MIN_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Discharge Current limit derived from Min-Cell-Voltage. * @@ -72,7 +76,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_DISCHARGE_MIN_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Charge Current limit derived from Max-Cell-Voltage. * @@ -83,7 +88,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_CHARGE_MAX_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Discharge Current limit derived from Max-Cell-Voltage. * @@ -94,7 +100,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_DISCHARGE_MAX_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Charge Current limit derived from Min-Cell-Temperature. * @@ -105,7 +112,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_CHARGE_MIN_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Discharge Current limit derived from Min-Cell-Temperature. * @@ -116,7 +124,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_DISCHARGE_MIN_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Charge Current limit derived from Max-Cell-Temperature. * @@ -127,7 +136,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_CHARGE_MAX_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Discharge Current limit derived from Max-Cell-Temperature. * @@ -138,7 +148,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_DISCHARGE_MAX_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Charge Max-Increase Current limit. * @@ -149,7 +160,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_CHARGE_INCREASE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Discharge Max-Increase Current limit. * @@ -160,7 +172,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ BP_DISCHARGE_INCREASE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE)), + .unit(Unit.AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Force-Discharge State. * @@ -170,7 +183,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Ampere * */ - BP_FORCE_DISCHARGE(Doc.of(AbstractForceChargeDischarge.State.values())), // + BP_FORCE_DISCHARGE(Doc.of(AbstractForceChargeDischarge.State.values()) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Force-Charge State. * @@ -180,7 +194,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Ampere * */ - BP_FORCE_CHARGE(Doc.of(AbstractForceChargeDischarge.State.values())) // + BP_FORCE_CHARGE(Doc.of(AbstractForceChargeDischarge.State.values()) // + .persistencePriority(PersistencePriority.MEDIUM)), // ; private final Doc doc; diff --git a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/DummyBattery.java b/io.openems.edge.battery.api/test/io/openems/edge/battery/api/DummyBattery.java deleted file mode 100644 index 952b486dfb9..00000000000 --- a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/DummyBattery.java +++ /dev/null @@ -1,157 +0,0 @@ -package io.openems.edge.battery.api; - -import io.openems.common.exceptions.NotImplementedException; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.edge.common.component.AbstractOpenemsComponent; -import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.startstop.StartStop; -import io.openems.edge.common.startstop.StartStoppable; - -// TODO merge with io.openems.edge.battery.test.DummyBattery -public class DummyBattery extends AbstractOpenemsComponent implements Battery, StartStoppable { - - public static final int DEFAULT_SOC = 50; - public static final int DEFAULT_CAPACITY = 50_000; - public static final int DEFAULT_VOLTAGE = 750; - public static final int DEFAULT_MIN_CELL_VOLTAGE = 3280; - public static final int DEFAULT_MAX_CELL_VOLTAGE = 3380; - public static final int DEFAULT_MIN_CELL_TEMPERATURE = 25; - public static final int DEFAULT_MAX_CELL_TEMPERATURE = 33; - public static final int DEFAULT_MAX_CHARGE_CURRENT = 50; - public static final int DEFAULT_MAX_DISCHARGE_CURRENT = 50; - - protected DummyBattery(// - ) { // - super(// - OpenemsComponent.ChannelId.values(), // - Battery.ChannelId.values(), // - StartStoppable.ChannelId.values() // - ); - - this.setMinimalCellVoltage(DEFAULT_MIN_CELL_VOLTAGE); - this.setMaximalCellVoltage(DEFAULT_MAX_CELL_VOLTAGE); - this.setMinimalCellTemperature(DEFAULT_MIN_CELL_TEMPERATURE); - this.setMaximalCellTemperature(DEFAULT_MAX_CELL_TEMPERATURE); - this.setSoc(DEFAULT_SOC); - this.setCapacity(DEFAULT_CAPACITY); - this.setVoltage(DEFAULT_VOLTAGE); - this.setChargeMaxCurrent(DEFAULT_MAX_CHARGE_CURRENT); - this.setDischargeMaxCurrent(DEFAULT_MAX_DISCHARGE_CURRENT); - } - - void setMinimalCellVoltage(int minimalCellVoltage) { - this._setMinCellVoltage(minimalCellVoltage); - this.getMinCellVoltageChannel().nextProcessImage(); - } - - void setMinimalCellVoltageToUndefined() { - this._setMinCellVoltage(null); - this.getMinCellVoltageChannel().nextProcessImage(); - } - - void setMaximalCellVoltage(int maximalCellVoltage) { - this._setMaxCellVoltage(maximalCellVoltage); - this.getMaxCellVoltageChannel().nextProcessImage(); - } - - void setMaximalCellVoltageToUndefined() { - this._setMaxCellVoltage(null); - this.getMaxCellVoltageChannel().nextProcessImage(); - } - - void setMinimalCellTemperature(int minimalCellTemperature) { - this._setMinCellTemperature(minimalCellTemperature); - this.getMinCellTemperatureChannel().nextProcessImage(); - } - - void setMinimalCellTemperatureToUndefined() { - this._setMinCellTemperature(null); - this.getMinCellTemperatureChannel().nextProcessImage(); - } - - void setMaximalCellTemperature(int maximalCellTemperature) { - this._setMaxCellTemperature(maximalCellTemperature); - this.getMaxCellTemperatureChannel().nextProcessImage(); - } - - void setMaximalCellTemperatureToUndefined() { - this._setMaxCellTemperature(null); - this.getMaxCellTemperatureChannel().nextProcessImage(); - } - - void setSoc(int soc) { - this._setSoc(soc); - this.getSocChannel().nextProcessImage(); - } - - void setSocToUndefined() { - this._setSoc(null); - this.getSocChannel().nextProcessImage(); - } - - void setVoltage(int voltage) { - this._setVoltage(voltage); - this.getVoltageChannel().nextProcessImage(); - } - - void setVoltageToUndefined() { - this._setVoltage(null); - this.getVoltageChannel().nextProcessImage(); - } - - void setCapacity(int capacity) { - this._setCapacity(capacity); - this.getCapacityChannel().nextProcessImage(); - } - - void setCapacityToUndefined() { - this._setCapacity(null); - this.getCapacityChannel().nextProcessImage(); - } - - void setForceDischargeActive(boolean active) { - this._setForceDischargeActive(active); - this.getForceDischargeActiveChannel().nextProcessImage(); - } - - void setForceDischargeActiveToUndefined() { - this._setForceDischargeActive(null); - this.getForceDischargeActiveChannel().nextProcessImage(); - } - - void setForceChargeActive(boolean active) { - this._setForceChargeActive(active); - this.getForceChargeActiveChannel().nextProcessImage(); - } - - void setForceChargeActiveToUndefined() { - this._setForceChargeActive(null); - this.getForceChargeActiveChannel().nextProcessImage(); - } - - void setChargeMaxCurrent(int value) { - this._setChargeMaxCurrent(value); - this.getChargeMaxCurrentChannel().nextProcessImage(); - } - - void setChargeMaxCurrentToUndefined() { - this._setChargeMaxCurrent(null); - this.getChargeMaxCurrentChannel().nextProcessImage(); - } - - void setDischargeMaxCurrent(int value) { - this._setDischargeMaxCurrent(value); - this.getDischargeMaxCurrentChannel().nextProcessImage(); - } - - void setDischargeMaxCurrentToUndefined() { - this._setDischargeMaxCurrent(null); - this.getDischargeMaxCurrentChannel().nextProcessImage(); - } - - @Override - public void setStartStop(StartStop value) throws OpenemsNamedException { - // TODO start stop is not implemented - throw new NotImplementedException("Start Stop is not implemented"); - } -} diff --git a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/DummyCellCharacteristic.java b/io.openems.edge.battery.api/test/io/openems/edge/battery/api/DummyCellCharacteristic.java deleted file mode 100644 index 51bc4507f46..00000000000 --- a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/DummyCellCharacteristic.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.openems.edge.battery.api; - -public class DummyCellCharacteristic implements CellCharacteristic { - - public static final int FINAL_CELL_CHARGE_VOLTAGE_MV = 3_650; - public static final int FINAL_CELL_DISCHARGE_VOLTAGE_MV = 2_900; - public static final int FORCE_CHARGE_CELL_VOLTAGE_MV = 2_800; - public static final int FORCE_DISCHARGE_CELL_VOLTAGE_MV = 3_680; - - @Override - public int getFinalCellChargeVoltage_mV() { - return FINAL_CELL_CHARGE_VOLTAGE_MV; - } - - @Override - public int getFinalCellDischargeVoltage_mV() { - return FINAL_CELL_DISCHARGE_VOLTAGE_MV; - } - - @Override - public int getForceChargeCellVoltage_mV() { - return FORCE_CHARGE_CELL_VOLTAGE_MV; - } - - @Override - public int getForceDischargeCellVoltage_mV() { - return FORCE_DISCHARGE_CELL_VOLTAGE_MV; - } - -} diff --git a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/SetAllowedCurrentsTest.java b/io.openems.edge.battery.api/test/io/openems/edge/battery/api/SetAllowedCurrentsTest.java deleted file mode 100644 index d6c38051c10..00000000000 --- a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/SetAllowedCurrentsTest.java +++ /dev/null @@ -1,469 +0,0 @@ -package io.openems.edge.battery.api; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; - -public class SetAllowedCurrentsTest { - - private DummyBattery battery; - private DummyCellCharacteristic cellCharacteristic; - private Settings settings; - - private static int MAX_INCREASE_MILLI_AMPERE = 300; - private static double MIN_CURRENT_AMPERE = 1; - private static int TOLERANCE_MILLI_VOLT = 10; - private static double POWER_FACTOR = 0.02; - - @Before - public void setUp() throws Exception { - this.battery = new DummyBattery(); - this.cellCharacteristic = new DummyCellCharacteristic(); - this.settings = new SettingsImpl(TOLERANCE_MILLI_VOLT, MIN_CURRENT_AMPERE, POWER_FACTOR, - MAX_INCREASE_MILLI_AMPERE); - } - - @Test - public void testBatteryIsChargedUntilFinalDischargeIsReached() { - // Battery has to be charged - int maxDischargeCurrentFromBms = 0; - int maxChargeCurrentFromBms = DummyBattery.DEFAULT_MAX_CHARGE_CURRENT; - this.battery.setMinimalCellVoltage(DummyCellCharacteristic.FORCE_CHARGE_CELL_VOLTAGE_MV); - - SetAllowedCurrents.setMaxAllowedCurrents(this.battery, this.cellCharacteristic, this.settings, - maxChargeCurrentFromBms, maxDischargeCurrentFromBms); - - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - int expectedMaxChargeCurrent = maxChargeCurrentFromBms; - int actualMaxChargeCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedMaxChargeCurrent, actualMaxChargeCurrent); - - int expectedMaxDischargeCurrent = -(int) Math.max(MIN_CURRENT_AMPERE, - this.battery.getCapacity().get() * POWER_FACTOR / this.battery.getVoltage().get()); - int actualMaxDischargeCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedMaxDischargeCurrent, actualMaxDischargeCurrent); - - boolean expectedChargeForce = true; - boolean actualChargeForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedChargeForce, actualChargeForce); - - boolean expectedDischargeForce = false; - boolean actualdischargeForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedDischargeForce, actualdischargeForce); - - // Min Voltage has risen above force level, but is still under final discharge - // level minus tolerance - this.battery.setMinimalCellVoltage( - DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV - this.settings.getToleranceMilliVolt() - 1); - SetAllowedCurrents.setMaxAllowedCurrents(this.battery, this.cellCharacteristic, this.settings, - maxChargeCurrentFromBms, maxDischargeCurrentFromBms); - - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - expectedMaxChargeCurrent = maxChargeCurrentFromBms; - actualMaxChargeCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedMaxChargeCurrent, actualMaxChargeCurrent); - - expectedMaxDischargeCurrent = -(int) Math.max(MIN_CURRENT_AMPERE, - this.battery.getCapacity().get() * POWER_FACTOR / this.battery.getVoltage().get()); - actualMaxDischargeCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedMaxDischargeCurrent, actualMaxDischargeCurrent); - - expectedChargeForce = true; - actualChargeForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedChargeForce, actualChargeForce); - - expectedDischargeForce = false; - actualdischargeForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedDischargeForce, actualdischargeForce); - - // Min Voltage has risen above final discharge level - this.battery.setMinimalCellVoltage(DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV + 1); - SetAllowedCurrents.setMaxAllowedCurrents(this.battery, this.cellCharacteristic, this.settings, - maxChargeCurrentFromBms, maxDischargeCurrentFromBms); - - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - expectedMaxChargeCurrent = maxChargeCurrentFromBms; - actualMaxChargeCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedMaxChargeCurrent, actualMaxChargeCurrent); - - expectedMaxDischargeCurrent = maxDischargeCurrentFromBms; - actualMaxDischargeCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedMaxDischargeCurrent, actualMaxDischargeCurrent); - - expectedChargeForce = false; - actualChargeForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedChargeForce, actualChargeForce); - - expectedDischargeForce = false; - actualdischargeForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedDischargeForce, actualdischargeForce); - } - - @Test - public void testSetMaxAllowedCurrents() { - // Nothing is necessary - int maxDischargeCurrentFromBms = DummyBattery.DEFAULT_MAX_DISCHARGE_CURRENT; - int maxChargeCurrentFromBms = DummyBattery.DEFAULT_MAX_CHARGE_CURRENT; - SetAllowedCurrents.setMaxAllowedCurrents(this.battery, this.cellCharacteristic, this.settings, - maxChargeCurrentFromBms, maxDischargeCurrentFromBms); - - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - int expectedMaxChargeCurrent = maxChargeCurrentFromBms; - int actualMaxChargeCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedMaxChargeCurrent, actualMaxChargeCurrent); - int expectedMaxDischargeCurrent = maxDischargeCurrentFromBms; - int actualMaxDischargeCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedMaxDischargeCurrent, actualMaxDischargeCurrent); - boolean expectedChargeForce = false; - boolean actualChargeForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedChargeForce, actualChargeForce); - boolean expectedDischargeForce = false; - boolean actualdischargeForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedDischargeForce, actualdischargeForce); - - // Battery has to be charged - maxDischargeCurrentFromBms = 0; - this.battery.setMinimalCellVoltage(DummyCellCharacteristic.FORCE_CHARGE_CELL_VOLTAGE_MV); - - SetAllowedCurrents.setMaxAllowedCurrents(this.battery, this.cellCharacteristic, this.settings, - maxChargeCurrentFromBms, maxDischargeCurrentFromBms); - - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - expectedMaxChargeCurrent = maxChargeCurrentFromBms; - actualMaxChargeCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedMaxChargeCurrent, actualMaxChargeCurrent); - - expectedMaxDischargeCurrent = -(int) Math.max(MIN_CURRENT_AMPERE, - this.battery.getCapacity().get() * POWER_FACTOR / this.battery.getVoltage().get()); - actualMaxDischargeCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedMaxDischargeCurrent, actualMaxDischargeCurrent); - expectedChargeForce = true; - actualChargeForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedChargeForce, actualChargeForce); - expectedDischargeForce = false; - actualdischargeForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedDischargeForce, actualdischargeForce); - } - - @Test - public void testSetChannelsForCharge() { - int expectedCurrent = DummyBattery.DEFAULT_MAX_CHARGE_CURRENT; - int actualCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - // Battery can be charged, no discharge necessary - int maxChargeCurrent = DummyBattery.DEFAULT_MAX_CHARGE_CURRENT + 1; - SetAllowedCurrents.setChannelsForCharge(maxChargeCurrent, this.battery); - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - - expectedCurrent = maxChargeCurrent; - actualCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - boolean expectedForce = false; - boolean actualForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedForce, actualForce); - - // Battery cannot be charged, no discharge necessary - maxChargeCurrent = 0; - SetAllowedCurrents.setChannelsForCharge(maxChargeCurrent, this.battery); - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - - expectedCurrent = maxChargeCurrent; - actualCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - expectedForce = false; - actualForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedForce, actualForce); - - // Battery cannot be charged, must be discharged - maxChargeCurrent = -8; - SetAllowedCurrents.setChannelsForCharge(maxChargeCurrent, this.battery); - this.battery.getChargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceDischargeActiveChannel().nextProcessImage(); - - expectedCurrent = maxChargeCurrent; - actualCurrent = this.battery.getChargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - expectedForce = true; - actualForce = this.battery.getForceDischargeActive().get(); - assertEquals(expectedForce, actualForce); - } - - @Test - public void testSetChannelsForDischarge() { - int expectedCurrent = DummyBattery.DEFAULT_MAX_DISCHARGE_CURRENT; - int actualCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - // Battery can be discharged, no charge necessary - int maxDischargeCurrent = DummyBattery.DEFAULT_MAX_DISCHARGE_CURRENT + 1; - SetAllowedCurrents.setChannelsForDischarge(maxDischargeCurrent, this.battery); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - expectedCurrent = maxDischargeCurrent; - actualCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - boolean expectedForce = false; - boolean actualForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedForce, actualForce); - - // Battery cannot be discharged, no charge necessary - maxDischargeCurrent = 0; - SetAllowedCurrents.setChannelsForDischarge(maxDischargeCurrent, this.battery); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - expectedCurrent = maxDischargeCurrent; - actualCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - expectedForce = false; - actualForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedForce, actualForce); - - // Battery cannot be charged, must be charged - maxDischargeCurrent = -8; - SetAllowedCurrents.setChannelsForDischarge(maxDischargeCurrent, this.battery); - this.battery.getDischargeMaxCurrentChannel().nextProcessImage(); - this.battery.getForceChargeActiveChannel().nextProcessImage(); - - expectedCurrent = maxDischargeCurrent; - actualCurrent = this.battery.getDischargeMaxCurrent().get(); - assertEquals(expectedCurrent, actualCurrent); - - expectedForce = true; - actualForce = this.battery.getForceChargeActive().get(); - assertEquals(expectedForce, actualForce); - } - - @Test - public void testIsVoltageLowerThanForceDischargeVoltage() { - - assertTrue(SetAllowedCurrents.isVoltageLowerThanForceDischargeVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage((DummyCellCharacteristic.FORCE_DISCHARGE_CELL_VOLTAGE_MV - 1)); - assertTrue(SetAllowedCurrents.isVoltageLowerThanForceDischargeVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage((DummyCellCharacteristic.FORCE_DISCHARGE_CELL_VOLTAGE_MV)); - assertFalse(SetAllowedCurrents.isVoltageLowerThanForceDischargeVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage((DummyCellCharacteristic.FORCE_DISCHARGE_CELL_VOLTAGE_MV + 1)); - assertFalse(SetAllowedCurrents.isVoltageLowerThanForceDischargeVoltage(this.cellCharacteristic, this.battery)); - } - - @Test - public void testIsVoltageAboveFinalChargingVoltage() { - assertFalse(SetAllowedCurrents.isVoltageAboveFinalChargingVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage((DummyCellCharacteristic.FINAL_CELL_CHARGE_VOLTAGE_MV - 1)); - assertFalse(SetAllowedCurrents.isVoltageAboveFinalChargingVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage((DummyCellCharacteristic.FINAL_CELL_CHARGE_VOLTAGE_MV)); - assertFalse(SetAllowedCurrents.isVoltageAboveFinalChargingVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage((DummyCellCharacteristic.FINAL_CELL_CHARGE_VOLTAGE_MV + 1)); - assertTrue(SetAllowedCurrents.isVoltageAboveFinalChargingVoltage(this.cellCharacteristic, this.battery)); - } - - @Test - public void testIsVoltageHigherThanForceChargeVoltage() { - assertTrue(SetAllowedCurrents.isVoltageHigherThanForceChargeVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMinimalCellVoltage((DummyCellCharacteristic.FORCE_CHARGE_CELL_VOLTAGE_MV - 1)); - assertFalse(SetAllowedCurrents.isVoltageHigherThanForceChargeVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMinimalCellVoltage((DummyCellCharacteristic.FORCE_CHARGE_CELL_VOLTAGE_MV)); - assertFalse(SetAllowedCurrents.isVoltageHigherThanForceChargeVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMinimalCellVoltage((DummyCellCharacteristic.FORCE_CHARGE_CELL_VOLTAGE_MV + 1)); - assertTrue(SetAllowedCurrents.isVoltageHigherThanForceChargeVoltage(this.cellCharacteristic, this.battery)); - } - - @Test - public void testIsVoltageBelowFinalDischargingVoltage() { - assertFalse(SetAllowedCurrents.isVoltageBelowFinalDischargingVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMinimalCellVoltage(DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV - 1); - assertTrue(SetAllowedCurrents.isVoltageBelowFinalDischargingVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMinimalCellVoltage((DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV)); - assertFalse(SetAllowedCurrents.isVoltageBelowFinalDischargingVoltage(this.cellCharacteristic, this.battery)); - - this.battery.setMinimalCellVoltage((DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV + 1)); - assertFalse(SetAllowedCurrents.isVoltageBelowFinalDischargingVoltage(this.cellCharacteristic, this.battery)); - } - - @Test - public void testIsFurtherDischargingNecessary() { - assertFalse(SetAllowedCurrents.isFurtherDischargingNecessary(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage(DummyCellCharacteristic.FINAL_CELL_CHARGE_VOLTAGE_MV + 1); - assertFalse(SetAllowedCurrents.isFurtherDischargingNecessary(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage(DummyCellCharacteristic.FORCE_DISCHARGE_CELL_VOLTAGE_MV + 1); - assertFalse(SetAllowedCurrents.isFurtherDischargingNecessary(this.cellCharacteristic, this.battery)); - - this.battery.setForceDischargeActive(false); - assertFalse(SetAllowedCurrents.isFurtherDischargingNecessary(this.cellCharacteristic, this.battery)); - - this.battery.setForceDischargeActive(true); - assertTrue(SetAllowedCurrents.isFurtherDischargingNecessary(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage(DummyCellCharacteristic.FINAL_CELL_CHARGE_VOLTAGE_MV + 1); - assertTrue(SetAllowedCurrents.isFurtherDischargingNecessary(this.cellCharacteristic, this.battery)); - - this.battery.setMaximalCellVoltage(DummyCellCharacteristic.FINAL_CELL_CHARGE_VOLTAGE_MV); - assertFalse(SetAllowedCurrents.isFurtherDischargingNecessary(this.cellCharacteristic, this.battery)); - } - - @Test - public void testIsDischargingAlready() { - assertFalse(SetAllowedCurrents.isDischargingAlready(this.battery)); - - this.battery.setForceDischargeActive(true); - assertTrue(SetAllowedCurrents.isDischargingAlready(this.battery)); - - this.battery.setForceDischargeActive(false); - assertFalse(SetAllowedCurrents.isDischargingAlready(this.battery)); - } - - @Test - public void testCalculateForceCurrent() { - int expected = -(int) Math.max(MIN_CURRENT_AMPERE, - DummyBattery.DEFAULT_CAPACITY * POWER_FACTOR / DummyBattery.DEFAULT_VOLTAGE); // 1.333 => 1 - assertEquals(expected, SetAllowedCurrents.calculateForceCurrent(this.battery, this.settings)); - - int newCapacity = 200_000; - this.battery.setCapacity(newCapacity); - // 5.333 => 5 - expected = -(int) Math.max(MIN_CURRENT_AMPERE, newCapacity * POWER_FACTOR / DummyBattery.DEFAULT_VOLTAGE); - assertEquals(expected, SetAllowedCurrents.calculateForceCurrent(this.battery, this.settings)); - - int newVoltage = 850; - this.battery.setCapacity(newCapacity); - this.battery.setVoltage(newVoltage); - expected = -(int) Math.max(MIN_CURRENT_AMPERE, newCapacity * POWER_FACTOR / newVoltage); // 4.706 => 4 - assertEquals(expected, SetAllowedCurrents.calculateForceCurrent(this.battery, this.settings)); - - newCapacity = 30_000; - newVoltage = 700; - this.battery.setCapacity(newCapacity); - this.battery.setVoltage(newVoltage); - expected = -(int) Math.max(MIN_CURRENT_AMPERE, newCapacity * POWER_FACTOR / newVoltage); // 0.857 => 1 - assertEquals(expected, SetAllowedCurrents.calculateForceCurrent(this.battery, this.settings)); - - newCapacity = 10_000; - this.battery.setCapacity(newCapacity); - this.battery.setVoltage(newVoltage); - expected = -(int) Math.max(MIN_CURRENT_AMPERE, newCapacity * POWER_FACTOR / newVoltage); // 0.286 => 1 - assertEquals(expected, SetAllowedCurrents.calculateForceCurrent(this.battery, this.settings)); - } - - @Test - public void testCalculateForceDischargeCurrent() { - int expected = -(int) Math.max(MIN_CURRENT_AMPERE, - DummyBattery.DEFAULT_CAPACITY * POWER_FACTOR / DummyBattery.DEFAULT_VOLTAGE); // 1.333 => 1 - assertEquals(expected, SetAllowedCurrents.calculateForceDischargeCurrent(this.battery, this.settings)); - } - - @Test - public void testCalculateForceChargeCurrent() { - int expected = -(int) Math.max(MIN_CURRENT_AMPERE, - DummyBattery.DEFAULT_CAPACITY * POWER_FACTOR / DummyBattery.DEFAULT_VOLTAGE); // 1.333 => 1 - assertEquals(expected, SetAllowedCurrents.calculateForceDischargeCurrent(this.battery, this.settings)); - } - - @Test - public void testIsFurtherChargingNecessary() { - assertFalse( - SetAllowedCurrents.isFurtherChargingNecessary(this.battery, this.cellCharacteristic, this.settings)); - - this.battery.setMinimalCellVoltage(DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV - 1); - assertFalse( - SetAllowedCurrents.isFurtherChargingNecessary(this.battery, this.cellCharacteristic, this.settings)); - - this.battery.setMinimalCellVoltage(DummyCellCharacteristic.FORCE_CHARGE_CELL_VOLTAGE_MV - 1); - assertFalse( - SetAllowedCurrents.isFurtherChargingNecessary(this.battery, this.cellCharacteristic, this.settings)); - - this.battery.setForceChargeActive(false); - assertFalse( - SetAllowedCurrents.isFurtherChargingNecessary(this.battery, this.cellCharacteristic, this.settings)); - - this.battery.setForceChargeActive(true); - assertTrue(SetAllowedCurrents.isFurtherChargingNecessary(this.battery, this.cellCharacteristic, this.settings)); - - this.battery.setMinimalCellVoltage( - DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV - this.settings.getToleranceMilliVolt() - 1); - assertTrue(SetAllowedCurrents.isFurtherChargingNecessary(this.battery, this.cellCharacteristic, this.settings)); - - this.battery.setMinimalCellVoltage( - DummyCellCharacteristic.FINAL_CELL_DISCHARGE_VOLTAGE_MV - this.settings.getToleranceMilliVolt()); - assertFalse( - SetAllowedCurrents.isFurtherChargingNecessary(this.battery, this.cellCharacteristic, this.settings)); - } - - @Test - public void testIsChargingAlready() { - assertFalse(SetAllowedCurrents.isChargingAlready(this.battery)); - - this.battery.setForceChargeActive(true); - assertTrue(SetAllowedCurrents.isChargingAlready(this.battery)); - - this.battery.setForceChargeActive(false); - assertFalse(SetAllowedCurrents.isChargingAlready(this.battery)); - } - - @Test - public void testAreApiValuesPresent() { - assertTrue(SetAllowedCurrents.areApiValuesPresent(this.battery)); - - this.battery.setCapacityToUndefined(); - assertFalse(SetAllowedCurrents.areApiValuesPresent(this.battery)); - - this.battery.setCapacity(DummyBattery.DEFAULT_CAPACITY); - this.battery.setVoltageToUndefined(); - assertFalse(SetAllowedCurrents.areApiValuesPresent(this.battery)); - - this.battery.setVoltage(DummyBattery.DEFAULT_VOLTAGE); - this.battery.setMinimalCellVoltageToUndefined(); - assertFalse(SetAllowedCurrents.areApiValuesPresent(this.battery)); - - this.battery.setMinimalCellVoltage(DummyBattery.DEFAULT_MIN_CELL_VOLTAGE); - this.battery.setMaximalCellVoltageToUndefined(); - assertFalse(SetAllowedCurrents.areApiValuesPresent(this.battery)); - - this.battery.setMaximalCellVoltage(DummyBattery.DEFAULT_MAX_CELL_VOLTAGE); - assertTrue(SetAllowedCurrents.areApiValuesPresent(this.battery)); - } -} \ No newline at end of file diff --git a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/SettingsImpl.java b/io.openems.edge.battery.api/test/io/openems/edge/battery/api/SettingsImpl.java deleted file mode 100644 index b6ec52ae835..00000000000 --- a/io.openems.edge.battery.api/test/io/openems/edge/battery/api/SettingsImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.openems.edge.battery.api; - -public class SettingsImpl implements Settings { - - private int toleranceMilliVolt; - private double minimumCurrentAmpere; - private double powerFactor; - private int maxIncreaseMilliAmpere; - - public SettingsImpl(int toleranceMilliVolt, double minimumCurrentAmpere, double powerFactor, - int maxIncreaseMilliAmpere) { - super(); - this.toleranceMilliVolt = toleranceMilliVolt; - this.minimumCurrentAmpere = minimumCurrentAmpere; - this.powerFactor = powerFactor; - this.maxIncreaseMilliAmpere = maxIncreaseMilliAmpere; - } - - @Override - public int getMaxIncreaseMilliAmpere() { - return this.maxIncreaseMilliAmpere; - } - - @Override - public double getPowerFactor() { - return this.powerFactor; - } - - @Override - public double getMinimumCurrentAmpere() { - return this.minimumCurrentAmpere; - } - - @Override - public int getToleranceMilliVolt() { - return this.toleranceMilliVolt; - } - -} diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130.java index acde6cce71f..f80ac6bb55c 100644 --- a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130.java +++ b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130.java @@ -162,10 +162,6 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { .unit(Unit.MILLIVOLT)), // SYSTEM_INSULATION(Doc.of(OpenemsType.INTEGER) // .unit(Unit.KILOOHM)), // - SYSTEM_ACCEPT_MAX_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // - SYSTEM_ACCEPT_MAX_DISCHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // CLUSTER_1_BATTERY_000_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT)), // CLUSTER_1_BATTERY_001_VOLTAGE(Doc.of(OpenemsType.INTEGER) // diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130Impl.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130Impl.java index a972015817f..10868f73131 100644 --- a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130Impl.java +++ b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryBoxC130Impl.java @@ -24,10 +24,10 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.battery.api.Battery; -import io.openems.edge.battery.api.SetAllowedCurrents; import io.openems.edge.battery.bydcommercial.statemachine.Context; import io.openems.edge.battery.bydcommercial.statemachine.StateMachine; import io.openems.edge.battery.bydcommercial.statemachine.StateMachine.State; +import io.openems.edge.battery.protection.BatteryProtection; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; @@ -38,6 +38,7 @@ import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC6WriteRegisterTask; +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.modbusslave.ModbusSlave; @@ -52,8 +53,8 @@ immediate = true, // configurationPolicy = ConfigurationPolicy.REQUIRE, // property = { // - EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, // - EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // }) public class BatteryBoxC130Impl extends AbstractOpenemsModbusComponent implements BatteryBoxC130, Battery, OpenemsComponent, EventHandler, ModbusSlave, StartStoppable { @@ -70,29 +71,25 @@ public class BatteryBoxC130Impl extends AbstractOpenemsModbusComponent @Reference protected ConfigurationAdmin cm; + @Reference + protected ComponentManager componentManager; + /** * Manages the {@link State}s of the StateMachine. */ private final StateMachine stateMachine = new StateMachine(State.UNDEFINED); private Config config; - private SetAllowedCurrents setAllowedCurrents; + private BatteryProtection batteryProtection = null; public BatteryBoxC130Impl() { super(// OpenemsComponent.ChannelId.values(), // Battery.ChannelId.values(), // StartStoppable.ChannelId.values(), // - BatteryBoxC130.ChannelId.values() // + BatteryBoxC130.ChannelId.values(), // + BatteryProtection.ChannelId.values() // ); - - this.setAllowedCurrents = new SetAllowedCurrents(// - this, // - new BydC130CellCharacteristic(), // - new SingleRackSettings(), // - this.channel(BatteryBoxC130.ChannelId.SYSTEM_ACCEPT_MAX_CHARGE_CURRENT), // - this.channel(BatteryBoxC130.ChannelId.SYSTEM_ACCEPT_MAX_DISCHARGE_CURRENT) // - ); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) @@ -108,6 +105,11 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept return; } + // Initialize Battery-Protection + this.batteryProtection = BatteryProtection.create(this) // + .applyBatteryProtectionDefinition(new BatteryProtectionDefinitionBydC130(), this.componentManager) // + .build(); + int maxVoltage = this.config.numberOfSlaves() * MAX_ALLOWED_VOLTAGE_PER_MODULE; _setChargeMaxVoltage(maxVoltage); @@ -131,11 +133,9 @@ public void handleEvent(Event event) { switch (event.getTopic()) { case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE: - - this.setAllowedCurrents.act(); - + this.batteryProtection.apply(); break; - + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: this.handleStateMachine(); break; @@ -318,10 +318,10 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { ), // new FC3ReadRegistersTask(0x216C, Priority.HIGH, // - m(BatteryBoxC130.ChannelId.SYSTEM_ACCEPT_MAX_CHARGE_CURRENT, new SignedWordElement(0x216C), // - ElementToChannelConverter.SCALE_FACTOR_2), // - m(BatteryBoxC130.ChannelId.SYSTEM_ACCEPT_MAX_DISCHARGE_CURRENT, new SignedWordElement(0x216D), // - ElementToChannelConverter.SCALE_FACTOR_2) // + m(BatteryProtection.ChannelId.BP_CHARGE_BMS, new UnsignedWordElement(0x216C), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(BatteryProtection.ChannelId.BP_DISCHARGE_BMS, new UnsignedWordElement(0x216D), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC3ReadRegistersTask(0x2183, Priority.LOW, // @@ -682,7 +682,7 @@ public StartStop getStartStopTarget() { /* * Handle incompatibility with old hardware protocol. * - * 'onRegister0x2100Update()' callback is called when register 0x2100 is read. + * 'onRegister0x2100Update()' callback is called when register 0x2100 is read. */ private boolean isModbusProtocolInitialized = false; diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryProtectionDefinitionBydC130.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryProtectionDefinitionBydC130.java new file mode 100644 index 00000000000..8af87ce25bc --- /dev/null +++ b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BatteryProtectionDefinitionBydC130.java @@ -0,0 +1,83 @@ +package io.openems.edge.battery.bydcommercial; + +import io.openems.edge.battery.protection.BatteryProtectionDefinition; +import io.openems.edge.battery.protection.force.ForceCharge; +import io.openems.edge.battery.protection.force.ForceDischarge; +import io.openems.edge.common.linecharacteristic.PolyLine; + +public class BatteryProtectionDefinitionBydC130 implements BatteryProtectionDefinition { + + @Override + public int getInitialBmsMaxEverChargeCurrent() { + return 80; // [A] + } + + @Override + public int getInitialBmsMaxEverDischargeCurrent() { + return 80; // [A] + } + + @Override + public PolyLine getChargeVoltageToPercent() { + return PolyLine.create() // + .addPoint(3000, 0.1) // + .addPoint(Math.nextUp(3000), 1) // + .addPoint(3350, 1) // + .addPoint(3450, 0.9999) // + .addPoint(3600, 0.02) // + .addPoint(Math.nextDown(3650), 0.02) // + .addPoint(3650, 0) // + .build(); + } + + @Override + public PolyLine getDischargeVoltageToPercent() { + return PolyLine.create() // + .addPoint(2900, 0) // + .addPoint(Math.nextUp(2900), 0.01) // + .addPoint(2920, 0.01) // + .addPoint(3000, 1) // + .addPoint(3700, 1) // + .addPoint(Math.nextUp(3700), 0) // + .build(); + } + + @Override + public PolyLine getChargeTemperatureToPercent() { + return PolyLine.create() // + .addPoint(0, 0) // + .addPoint(Math.nextUp(0), 0.01) // + .addPoint(18, 1) // + .addPoint(35, 1) // + .addPoint(Math.nextDown(40), 0.01) // + .addPoint(40, 0) // + .build(); + } + + @Override + public PolyLine getDischargeTemperatureToPercent() { + return PolyLine.create() // + .addPoint(0, 0) // + .addPoint(Math.nextUp(0), 0.01) // + .addPoint(12, 1) // + .addPoint(45, 1) // + .addPoint(Math.nextDown(55), 0.01) // + .addPoint(55, 0) // + .build(); + } + + @Override + public ForceDischarge.Params getForceDischargeParams() { + return new ForceDischarge.Params(3660, 3640, 3450); + } + + @Override + public ForceCharge.Params getForceChargeParams() { + return new ForceCharge.Params(2850, 2910, 3000); + } + + @Override + public Double getMaxIncreaseAmperePerSecond() { + return 0.1; // [A] per second + } +} \ No newline at end of file diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydC130CellCharacteristic.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydC130CellCharacteristic.java deleted file mode 100644 index 132dedcbbc0..00000000000 --- a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydC130CellCharacteristic.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.openems.edge.battery.bydcommercial; - -import io.openems.edge.battery.api.CellCharacteristic; - -public class BydC130CellCharacteristic implements CellCharacteristic { - - @Override - public int getFinalCellDischargeVoltage_mV() { - return 2_900; - } - - @Override - public int getForceChargeCellVoltage_mV() { - return 2_850; - } - - @Override - public int getFinalCellChargeVoltage_mV() { - return 3_650; - } - - @Override - public int getForceDischargeCellVoltage_mV() { - return 3_680; - } - -} \ No newline at end of file diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/SingleRackSettings.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/SingleRackSettings.java deleted file mode 100644 index 869fd44da87..00000000000 --- a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/SingleRackSettings.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.openems.edge.battery.bydcommercial; - -import io.openems.edge.battery.api.Settings; - -public class SingleRackSettings implements Settings { - - private static final double POWER_FACTOR = 0.02; - private static final int MINIMUM_CURRENT_AMPERE = 1; - private static final int TOLERANCE_MILLI_VOLT = 10; - private static final int MAX_INCREASE_MILLIAMPERE = 300; - - @Override - public int getMaxIncreaseMilliAmpere() { - return MAX_INCREASE_MILLIAMPERE; - } - - @Override - public double getPowerFactor() { - return POWER_FACTOR; - } - - @Override - public double getMinimumCurrentAmpere() { - return MINIMUM_CURRENT_AMPERE; - } - - @Override - public int getToleranceMilliVolt() { - return TOLERANCE_MILLI_VOLT; - } - -} diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/ChannelIdImpl.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/ChannelIdImpl.java new file mode 100644 index 00000000000..641276b596d --- /dev/null +++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/ChannelIdImpl.java @@ -0,0 +1,29 @@ +package io.openems.edge.battery.fenecon.home; + +import io.openems.edge.common.channel.ChannelId; +import io.openems.edge.common.channel.Doc; + +/** + * This class is used to create Cell Voltage and Temperature Dynamic Channels. + */ +public class ChannelIdImpl implements ChannelId { + ; + + private final String name; + private final Doc doc; + + public ChannelIdImpl(String name, Doc doc) { + this.name = name; + this.doc = doc; + } + + @Override + public String name() { + return this.name; + } + + @Override + public Doc doc() { + return this.doc; + } +} diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBattery.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBattery.java index ae67b95e7d8..23cb716f4db 100644 --- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBattery.java +++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBattery.java @@ -3,13 +3,12 @@ import io.openems.common.channel.AccessMode; import io.openems.common.channel.Level; import io.openems.common.channel.Unit; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.OpenemsType; import io.openems.edge.battery.api.Battery; import io.openems.edge.battery.fenecon.home.enums.BmsControl; -import io.openems.edge.battery.fenecon.home.enums.State; +import io.openems.edge.battery.fenecon.home.statemachine.StateMachine.State; +import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.WriteChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.startstop.StartStop; @@ -22,7 +21,7 @@ public interface FeneconHomeBattery extends Battery, OpenemsComponent, StartStop * * @return the Channel */ - public default WriteChannel getBmsControlChannel() { + public default Channel getBmsControlChannel() { return this.channel(ChannelId.BMS_CONTROL); } @@ -45,16 +44,6 @@ public default void _setBmsControl(BmsControl value) { this.getBmsControlChannel().setNextValue(value); } - /** - * Writes the value to the {@link ChannelId#BMS_CONTROL} Register. - * - * @param value the next value - * @throws OpenemsNamedException on error - */ - public default void setBmsControl(BmsControl value) throws OpenemsNamedException { - this.getBmsControlChannel().setNextWriteValue(value); - } - /** * Gets the target Start/Stop mode from config or StartStop-Channel. * @@ -64,793 +53,636 @@ public default void setBmsControl(BmsControl value) throws OpenemsNamedException public static enum ChannelId implements io.openems.edge.common.channel.ChannelId { // EnumWriteChannels - RACK_PRE_ALARM_CELL_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_CELL_OVER_VOLTAGE(Doc.of(Level.INFO) // .text("Rack Cell Over Voltage Alarm")), // - RACK_PRE_ALARM_CELL_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_CELL_UNDER_VOLTAGE(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell Under Voltage Alarm")), // - RACK_PRE_ALARM_OVER_CHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_OVER_CHARGING_CURRENT(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Charging Current Alarm")), // - RACK_PRE_ALARM_OVER_DISCHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_OVER_DISCHARGING_CURRENT(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Discharging Current Alarm")), // - RACK_PRE_ALARM_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_OVER_TEMPERATURE(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Temperature Alarm")), // - RACK_PRE_ALARM_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_UNDER_TEMPERATURE(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Under Temperature Alarm")), // - RACK_PRE_ALARM_CELL_VOLTAGE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_CELL_VOLTAGE_DIFFERENCE(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell VOltage Difference Alarm")), // - RACK_PRE_ALARM_BCU_TEMP_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_BCU_TEMP_DIFFERENCE(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack BCU Temp Difference Alarm")), // - RACK_PRE_ALARM_UNDER_SOC(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_UNDER_SOC(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Under SOC Alarm")), // - RACK_PRE_ALARM_UNDER_SOH(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_UNDER_SOH(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Under SOH Alarm")), // - RACK_PRE_ALARM_OVER_CHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_OVER_CHARGING_POWER(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Charging Alarm")), // - RACK_PRE_ALARM_OVER_DISCHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // + RACK_PRE_ALARM_OVER_DISCHARGING_POWER(Doc.of(Level.INFO) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Discharging Alarm")), // - RACK_LEVEL_1_CELL_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_CELL_OVER_VOLTAGE(Doc.of(Level.WARNING) // .text("Rack Cell Over Voltage warning")), // - RACK_LEVEL_1_CELL_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_CELL_UNDER_VOLTAGE(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell Under Voltage warning")), // - RACK_LEVEL_1_OVER_CHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_OVER_CHARGING_CURRENT(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Charging Current warning")), // - RACK_LEVEL_1_OVER_DISCHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_OVER_DISCHARGING_CURRENT(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Discharging Current warning")), // - RACK_LEVEL_1_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_OVER_TEMPERATURE(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Temperature warning")), // - RACK_LEVEL_1_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_UNDER_TEMPERATURE(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Under Temperature warning")), // - RACK_LEVEL_1_CELL_VOLTAGE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_CELL_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell VOltage Difference warning")), // - RACK_LEVEL_1_BCU_TEMP_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_BCU_TEMP_DIFFERENCE(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack BCU Temp Difference warning")), // - RACK_LEVEL_1_UNDER_SOC(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_UNDER_SOC(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Under SOC warning")), // - RACK_LEVEL_1_UNDER_SOH(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_UNDER_SOH(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Under SOH warning")), // - RACK_LEVEL_1_OVER_CHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_OVER_CHARGING_POWER(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Charging warning")), // - RACK_LEVEL_1_OVER_DISCHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_1_OVER_DISCHARGING_POWER(Doc.of(Level.WARNING) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Discharging warning")), // - RACK_LEVEL_2_CELL_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_CELL_OVER_VOLTAGE(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell Over Voltage Fault")), // - RACK_LEVEL_2_CELL_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_CELL_UNDER_VOLTAGE(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell Under Voltage Fault")), // - RACK_LEVEL_2_OVER_CHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_OVER_CHARGING_CURRENT(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Charging Current Fault")), // - RACK_LEVEL_2_OVER_DISCHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_OVER_DISCHARGING_CURRENT(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Discharging Current Fault")), // - RACK_LEVEL_2_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_OVER_TEMPERATURE(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Over Temperature Fault")), // - RACK_LEVEL_2_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_UNDER_TEMPERATURE(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Under Temperature Fault")), // - RACK_LEVEL_2_CELL_VOLTAGE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_CELL_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell Voltage Difference Fault")), // - RACK_LEVEL_2_BCU_TEMP_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_BCU_TEMP_DIFFERENCE(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack BCU Temp Difference Fault")), // - RACK_LEVEL_2_CELL_TEMPERATURE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_CELL_TEMPERATURE_DIFFERENCE(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Cell Temperature Difference Fault")), // - RACK_LEVEL_2_INTERNAL_COMMUNICATION(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_INTERNAL_COMMUNICATION(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Internal Communication Fault")), // - RACK_LEVEL_2_EXTERNAL_COMMUNICATION(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_EXTERNAL_COMMUNICATION(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack External Communication Fault")), // - RACK_LEVEL_2_PRE_CHARGE_FAIL(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_PRE_CHARGE_FAIL(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Pre Charge Fault")), // - RACK_LEVEL_2_PARALLEL_FAIL(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_PARALLEL_FAIL(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Parallel Fault")), // - RACK_LEVEL_2_SYSTEM_FAIL(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_SYSTEM_FAIL(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack System Fault")), // - RACK_LEVEL_2_HARDWARE_FAIL(Doc.of(OpenemsType.BOOLEAN) // + RACK_LEVEL_2_HARDWARE_FAIL(Doc.of(Level.FAULT) // .accessMode(AccessMode.READ_ONLY) // .text("Rack Hardware Fault")), // - ALARM_POSITION_BCU_1(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Alarm BCU Position + ALARM_POSITION_BCU_1(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 1 Position ")), // - ALARM_POSITION_BCU_2(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_2(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 2 Position ")), // - ALARM_POSITION_BCU_3(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_3(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 3 Position ")), // - ALARM_POSITION_BCU_4(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_4(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 4 Position ")), // - ALARM_POSITION_BCU_5(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_5(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 5 Position ")), // - ALARM_POSITION_BCU_6(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_6(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 6 Position ")), // - ALARM_POSITION_BCU_7(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_7(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 7 Position ")), // - ALARM_POSITION_BCU_8(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_8(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 8 Position ")), // - ALARM_POSITION_BCU_9(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_9(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 9 Position ")), // - ALARM_POSITION_BCU_10(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + ALARM_POSITION_BCU_10(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("Alarm BCU 10 Position ")), // - WARNING_POSITION_BCU_1(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Warning BCU Position + WARNING_POSITION_BCU_1(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 1 Position ")), // - WARNING_POSITION_BCU_2(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_2(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 2 Position ")), // - WARNING_POSITION_BCU_3(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_3(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 3 Position ")), // - WARNING_POSITION_BCU_4(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_4(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 4 Position ")), // - WARNING_POSITION_BCU_5(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_5(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 5 Position ")), // - WARNING_POSITION_BCU_6(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_6(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 6 Position ")), // - WARNING_POSITION_BCU_7(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_7(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 7 Position ")), // - WARNING_POSITION_BCU_8(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_8(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 8 Position ")), // - WARNING_POSITION_BCU_9(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_9(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 9 Position ")), // - WARNING_POSITION_BCU_10(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + WARNING_POSITION_BCU_10(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Warning BCU 10 Position ")), // - FAULT_POSITION_BCU_1(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Fault BCU Position + FAULT_POSITION_BCU_1(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 1 Position ")), // - FAULT_POSITION_BCU_2(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_2(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 2 Position ")), // - FAULT_POSITION_BCU_3(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_3(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 3 Position ")), // - FAULT_POSITION_BCU_4(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_4(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 4 Position ")), // - FAULT_POSITION_BCU_5(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_5(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 5 Position ")), // - FAULT_POSITION_BCU_6(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_6(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 6 Position ")), // - FAULT_POSITION_BCU_7(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_7(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 7 Position ")), // - FAULT_POSITION_BCU_8(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_8(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 8 Position ")), // - FAULT_POSITION_BCU_9(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_9(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 9 Position ")), // - FAULT_POSITION_BCU_10(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + FAULT_POSITION_BCU_10(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Fault BCU 10 Position ")), // - BATTERY_RACK_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("Battery Rack Voltage")), // - BATTERY_RACK_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // - .text("Battery Rack Current")), // - BATTERY_RACK_SOC(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT) // - .accessMode(AccessMode.READ_WRITE) // - .text("Battery Rack State Of Charge")), // - BATTERY_RACK_SOH(Doc.of(OpenemsType.INTEGER)// - .unit(Unit.PERCENT) // - .accessMode(AccessMode.READ_WRITE) // - .text("Battery Rack State Of Health")), // - CELL_VOLTAGE_MIN(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("Min Cell Voltage of All Module")), // + ID_OF_CELL_VOLTAGE_MIN(Doc.of(OpenemsType.INTEGER) // .unit(Unit.NONE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Id. (Min Cell Voltage)")), // - CELL_VOLTAGE_MAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("Cell Voltage MAX")), // ID_OF_CELL_VOLTAGE_MAX(Doc.of(OpenemsType.INTEGER) // .unit(Unit.NONE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Id. (Max Cell Voltage)")), // MIN_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.DEGREE_CELSIUS) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Min Temperature of Battery Rack")), // ID_OF_MIN_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.NONE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Id. (Min Temp)")), // MAX_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Max Temperature of Battery Rack")), // ID_OF_MAX_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Id. (Max Temp)")), // - MAX_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // - .text("Battery Rack DC Charge Current Limit")), // - MAX_DISCHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // - .text("Battery Rack DC Discharge Current Limit")), MAX_DC_CHARGE_CURRENT_LIMIT_PER_BCU(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Max Charge Current Limit Per BCU")), // MAX_DC_DISCHARGE_CURRENT_LIMIT_PER_BCU(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Max Discharge Current Limit Per BCU")), RACK_NUMBER_OF_BATTERY_BCU(Doc.of(OpenemsType.INTEGER) // .unit(Unit.NONE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Count Of The Connected BCU")), RACK_NUMBER_OF_CELLS_IN_SERIES_PER_MODULE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.NONE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack Number Of Cells in Series Per Module")), RACK_MAX_CELL_VOLTAGE_LIMIT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack Upper Cell Voltage Border -> System will stop charging if a cell reaches this voltage value")), RACK_MIN_CELL_VOLTAGE_LIMIT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack Lower Cell Voltage Border -> System will stop discharging if a cell reaches this voltage value")), - RACK_HW_AFE_COMMUNICATION_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Rack HW Fault Detail + RACK_HW_AFE_COMMUNICATION_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW AFE Communication Fault")), - RACK_HW_ACTOR_DRIVER_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_ACTOR_DRIVER_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW Actor Driver Fault")), - RACK_HW_EEPROM_COMMUNICATION_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_EEPROM_COMMUNICATION_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW EEPROM Communication Fault")), - RACK_HW_VOLTAGE_DETECT_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_VOLTAGE_DETECT_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW Voltage Detect Voltage")), - RACK_HW_TEMPERATURE_DETECT_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_TEMPERATURE_DETECT_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW Temperature Detect Fault")), - RACK_HW_CURRENT_DETECT_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_CURRENT_DETECT_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW Current Detect Fault")), - RACK_HW_ACTOR_NOT_CLOSE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_ACTOR_NOT_CLOSE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW Actor Not Close")), - RACK_HW_ACTOR_NOT_OPEN(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_ACTOR_NOT_OPEN(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW Actor Not Open")), - RACK_HW_FUSE_BROKEN(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_HW_FUSE_BROKEN(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack HW Fuse Broken")), - RACK_SYSTEM_AFE_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Rack System Fault Detail + RACK_SYSTEM_AFE_OVER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System AFE Over Temperature")), - RACK_SYSTEM_AFE_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_AFE_UNDER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System AFE Under Temperature")), - RACK_SYSTEM_AFE_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_AFE_OVER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System AFE Over Voltage")), - RACK_SYSTEM_AFE_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_AFE_UNDER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System AFE Over Temperature")), - RACK_SYSTEM_HIGH_TEMPERATURE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_HIGH_TEMPERATURE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System High Temperature Permanent Failure")), - RACK_SYSTEM_LOW_TEMPERATURE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_LOW_TEMPERATURE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System Low Temperature Permanent Failure")), - RACK_SYSTEM_HIGH_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_HIGH_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System High Cell Voltage Permanent Failure")), - RACK_SYSTEM_LOW_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_LOW_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System Low Cell Voltage Permanent Failure")), - RACK_SYSTEM_SHORT_CIRCUIT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + RACK_SYSTEM_SHORT_CIRCUIT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("Rack System Low Cell Voltage Permanent Failure")), UPPER_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT) // .accessMode(AccessMode.READ_WRITE) // .text("CV Point")), - BCU_BMS_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("Bcu Bms Software Version")), - BCU_BMS_HARDWARE_VERSION(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("Bcu Bms Hardware Version")), - BCU_STATUS_ALARM(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // BCU Status Flags + STATUS_ALARM(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status Alarm")), - BCU_STATUS_WARNING(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_WARNING(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status WARNNG")), - BCU_STATUS_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status BCU Status Fault")), - BCU_STATUS_PFET(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_PFET(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status Pre-Charge FET On/Off")), - BCU_STATUS_CFET(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_CFET(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status Charge FET On/Off")), - BCU_STATUS_DFET(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_DFET(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status Discharge FET On/Off")), - BCU_STATUS_BATTERY_IDLE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_BATTERY_IDLE(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status Battery Idle")), - BCU_STATUS_BATTERY_CHARGING(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_BATTERY_CHARGING(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status Battery Charging")), - BCU_STATUS_BATTERY_DISCHARGING(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + STATUS_BATTERY_DISCHARGING(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Status Battery Discharging")), - BCU_PRE_ALARM_CELL_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Bcu Alarm Flags + PRE_ALARM_CELL_OVER_VOLTAGE(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Cell Over Voltage")), - BCU_PRE_ALARM_CELL_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_CELL_UNDER_VOLTAGE(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Cell Under Voltage")), - BCU_PRE_ALARM_OVER_CHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_OVER_CHARGING_CURRENT(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Over Charging Current")), - BCU_PRE_ALARM_OVER_DISCHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_OVER_DISCHARGING_CURRENT(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Over Discharging Current")), - BCU_PRE_ALARM_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_OVER_TEMPERATURE(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Over Temperature")), - BCU_PRE_ALARM_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_UNDER_TEMPERATURE(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Under Temperature")), - BCU_PRE_ALARM_CELL_VOLTAGE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_CELL_VOLTAGE_DIFFERENCE(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Cell Voltage Difference")), - BCU_PRE_ALARM_BCU_TEMP_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_BCU_TEMP_DIFFERENCE(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm BCU Temperature Difference")), - BCU_PRE_ALARM_UNDER_SOC(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_UNDER_SOC(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Under SOC")), - BCU_PRE_ALARM_UNDER_SOH(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_UNDER_SOH(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Under SOH")), - BCU_PRE_ALARM_OVER_CHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_OVER_CHARGING_POWER(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Over Charging Power")), - BCU_PRE_ALARM_OVER_DISCHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + PRE_ALARM_OVER_DISCHARGING_POWER(Doc.of(Level.INFO) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Alarm Over Discharging Power")), - BCU_LEVEL_1_CELL_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Bcu Warning Flags + LEVEL_1_CELL_OVER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Cell Over Voltage")), - BCU_LEVEL_1_CELL_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_CELL_UNDER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Cell Under Voltage")), - BCU_LEVEL_1_OVER_CHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_OVER_CHARGING_CURRENT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Over Charging Current")), - BCU_LEVEL_1_OVER_DISCHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_OVER_DISCHARGING_CURRENT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Over Discharging Current")), - BCU_LEVEL_1_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_OVER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Over Temperature")), - BCU_LEVEL_1_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_UNDER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Under Temperature")), - BCU_LEVEL_1_CELL_VOLTAGE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_CELL_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Cell Voltage Difference")), - BCU_LEVEL_1_BCU_TEMP_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_BCU_TEMP_DIFFERENCE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning BCU Temperature Difference")), - BCU_LEVEL_1_UNDER_SOC(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_UNDER_SOC(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Under SOC")), - BCU_LEVEL_1_UNDER_SOH(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_UNDER_SOH(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Under SOH")), - BCU_LEVEL_1_OVER_CHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_OVER_CHARGING_POWER(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Over Charging Power")), - BCU_LEVEL_1_OVER_DISCHARGING_POWER(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_1_OVER_DISCHARGING_POWER(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Warning Over Discharging Power")), - BCU_LEVEL_2_CELL_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Bcu Fault Flags + LEVEL_2_CELL_OVER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Cell Over Voltage")), - BCU_LEVEL_2_CELL_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_CELL_UNDER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Cell Under Voltage")), - BCU_LEVEL_2_OVER_CHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_OVER_CHARGING_CURRENT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Over Charging Current")), - BCU_LEVEL_2_OVER_DISCHARGING_CURRENT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_OVER_DISCHARGING_CURRENT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Over Discharging Current")), - BCU_LEVEL_2_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_OVER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Over Temperature")), - BCU_LEVEL_2_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_UNDER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Under Temperature")), - BCU_LEVEL_2_CELL_VOLTAGE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_CELL_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Cell Voltage Difference")), - BCU_LEVEL_2_BCU_TEMP_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_BCU_TEMP_DIFFERENCE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault BCU Temperature Difference")), - BCU_LEVEL_2_TEMPERATURE_DIFFERENCE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_TEMPERATURE_DIFFERENCE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault BCU Temperature Difference")), - BCU_LEVEL_2_INTERNAL_COMMUNICATION(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_INTERNAL_COMMUNICATION(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Internal Communication")), - BCU_LEVEL_2_EXTERNAL_COMMUNICATION(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_EXTERNAL_COMMUNICATION(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault External Communication")), - BCU_LEVEL_2_PRECHARGE_FAIL(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_PRECHARGE_FAIL(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Pre-Charge Fail")), - BCU_LEVEL_2_PARALLEL_FAIL(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_PARALLEL_FAIL(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Parallel Fail")), - BCU_LEVEL_2_SYSTEM_FAIL(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_SYSTEM_FAIL(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault System Fault")), - BCU_LEVEL_2_HARDWARE_FAIL(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + LEVEL_2_HARDWARE_FAIL(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Fault Hardware Fault")), - BCU_HW_AFE_COMMUNICAITON_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + + // Bcu HW Fault Detail + HW_AFE_COMMUNICAITON_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW AFE Communication Fault")), - BCU_HW_ACTOR_DRIVER_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + HW_ACTOR_DRIVER_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW Actor Driver Fault")), - BCU_HW_EEPROM_COMMUNICATION_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + HW_EEPROM_COMMUNICATION_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW EEPROM Communication Fault")), - BCU_HW_VOLTAGE_DETECT_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + HW_VOLTAGE_DETECT_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW Voltage Detect Fault")), - BCU_HW_TEMPERATURE_DETECT_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + HW_TEMPERATURE_DETECT_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW Temperaure Detect Fault")), - BCU_HW_CURRENT_DETECT_FAULT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + HW_CURRENT_DETECT_FAULT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW Current Detect Fault")), - BCU_HW_ACTOR_NOT_CLOSE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + HW_ACTOR_NOT_CLOSE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW Actor Not Close Fault")), - BCU_HW_ACTOR_NOT_OPEN(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + HW_ACTOR_NOT_OPEN(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU HW Actor Not Open")), - BCU_HW_FUSE_BROKEN(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // - .text("BCU HW Fuse Broken Fault ")), - BCU_SYSTEM_AFE_OVER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE)// + HW_FUSE_BROKEN(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // + .text("BCU HW Fuse Broken Fault")), + + // Bcu System Fault Detail + SYSTEM_AFE_OVER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY)// .text("BCU System AFE Over Temperature Fault")), - BCU_SYSTEM_AFE_UNDER_TEMPERATURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_AFE_UNDER_TEMPERATURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System AFE Under Temperature Fault")), - BCU_SYSTEM_AFE_OVER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_AFE_OVER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System AFE Over Voltage Fault")), - BCU_SYSTEM_AFE_UNDER_VOLTAGE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_AFE_UNDER_VOLTAGE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System AFE Under Voltage Fault")), - BCU_SYSTEM_HIGH_TEMPERATURE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_HIGH_TEMPERATURE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System High Temperature Permanent Fault")), - BCU_SYSTEM_LOW_TEMPERATURE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_LOW_TEMPERATURE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System Low Temperature Permanent Fault")), - BCU_SYSTEM_HIGH_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_HIGH_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System High Cell Voltage Permanent Fault")), - BCU_SYSTEM_LOW_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_LOW_CELL_VOLTAGE_PERMANENT_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System Low Cell Voltage Permanent Fault")), - BCU_SYSTEM_LOW_CELL_VOLTAGE_FAILURE(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + BCU_SYSTEM_LOW_CELL_VOLTAGE_FAILURE(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System Low Cell Voltage Permanent Fault")), - BCU_SYSTEM_SHORT_CIRCUIT(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_WRITE) // + SYSTEM_SHORT_CIRCUIT(Doc.of(Level.WARNING) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU System Short Circuit Fault")), - BCU_SOC(Doc.of(OpenemsType.INTEGER) // + + // Rest of the Bcu registers + TOWER_SOC(Doc.of(OpenemsType.INTEGER) // .unit(Unit.PERCENT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU SOC")), BCU_SOH(Doc.of(OpenemsType.INTEGER) // .unit(Unit.PERCENT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU SOH")), BCU_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Battery BCU Voltage")), BCU_CURRENT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Battery BCU Current")), BCU_MIN_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Min Cell Voltage")), BCU_MAX_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Maxc Cell Voltage")), - BCU_AVERAGE_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + AVERAGE_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Average Of All Cell Voltages")), BCU_MAX_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU DC Charge Current Limit")), - BCU_MIN_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // + MIN_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.AMPERE) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU DC Discharge Current Limit")), BMS_SERIAL_NUMBER(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BMS Serial Number")), NO_OF_CYCLES(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Number Of Full charged/discharged cycles")), DESIGN_CAPACITY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE_HOURS) // - .accessMode(AccessMode.READ_WRITE) // + .unit(Unit.WATT_HOURS) // + .accessMode(AccessMode.READ_ONLY) // .text("Design Capacity Of the Module")), - USABLE_CAPACITY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE_HOURS) // - .accessMode(AccessMode.READ_WRITE) // - .text("Usable Capacity Of The Module")), + USEABLE_CAPACITY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .text("Useable Cpacity Of The Module")), REMAINING_CAPACITY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE_HOURS) // - .accessMode(AccessMode.READ_WRITE) // - .text("Remaining Capacity Of The Module")), - BCU_MAX_CELL_VOLTAGE_LIMIT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .text("Remaning Cpacity Of The Module")), + MAX_CELL_VOLTAGE_LIMIT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Max Cell Voltage Limit")), - BCU_MIN_CELL_VOLTAGE_LIMIT(Doc.of(OpenemsType.INTEGER) // + MIN_CELL_VOLTAGE_LIMIT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BCU Min Cell Voltage Limit")), BMU_NUMBER(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Bmu Number")), BMU_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BMU Software Version")), BMU_HARDWARE_VERSION(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BMU Hardware Version")), - CELL_1_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 1")), - CELL_2_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 2")), - CELL_3_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 3")), - CELL_4_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 4")), - CELL_5_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 5")), - CELL_6_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 6")), - CELL_7_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 7")), - CELL_8_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 8")), - CELL_9_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 9")), - CELL_10_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 10")), - CELL_11_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 11")), - CELL_12_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 12")), - CELL_13_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 13")), - CELL_14_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 14")), - CELL_15_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 15")), - CELL_16_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell Voltage of Block 16")), - BMU_TEMPERATURE_1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 1")), - BMU_TEMPERATURE_2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 2")), - BMU_TEMPERATURE_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 3")), - BMU_TEMPERATURE_4(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 4")), - BMU_TEMPERATURE_5(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 5")), - BMU_TEMPERATURE_6(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 6")), - BMU_TEMPERATURE_7(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 7")), - BMU_TEMPERATURE_8(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Temperature 8")), - BMU_CELL_1_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 1 Balancing Status ")), - BMU_CELL_2_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 2 Balancing Status ")), - BMU_CELL_3_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 3 Balancing Status ")), - BMU_CELL_4_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 4 Balancing Status ")), - BMU_CELL_5_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 5 Balancing Status ")), - BMU_CELL_6_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 6 Balancing Status ")), - BMU_CELL_7_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 7 Balancing Status ")), - BMU_CELL_8_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 8 Balancing Status ")), - BMU_CELL_9_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 9 Balancing Status ")), - BMU_CELL_10_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 10 Balancing Status ")), - BMU_CELL_11_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 11 Balancing Status ")), - BMU_CELL_12_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 12 Balancing Status ")), - BMU_CELL_13_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 13 Balancing Status ")), - BMU_CELL_14_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 14 Balancing Status ")), - BMU_CELL_15_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 15 Balancing Status ")), - BMU_CELL_16_BALANCING_STATUS(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // - .text("BMU Cell 16 Balancing Status ")), BMU_MAX_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT)// - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BMU Max Cell Voltage")), BMU_MIN_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT)// - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BMU Min Cell Voltage")), BMU_MAX_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Max BMU Temperature")), BMU_MIN_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.DEGREE_CELSIUS)// - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Min BMU Temperature")), SUM_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)// - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("BMU Sum Voltage")), - BMS_CONTROL(Doc.of(OpenemsType.INTEGER) // - .text("BMS CONTROL(1: Shutdown, 0: no action)")), + BMS_CONTROL(Doc.of(BmsControl.values()) // + .text("BMS CONTROL(1: Shutdown, 0: no action, 2: Ignore)")), KEEP_FET_OPEN(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_WRITE) // + .accessMode(AccessMode.READ_ONLY) // .text("Keep FET Open (Disconnect the relay; 1:Keep open, 0: normal operation)")), STATE_MACHINE(Doc.of(State.values()) // .text("Current State of State-Machine")), // diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryImpl.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryImpl.java index 6aac5c6f693..e16e85f2543 100644 --- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryImpl.java +++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryImpl.java @@ -1,5 +1,6 @@ package io.openems.edge.battery.fenecon.home; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import org.osgi.service.cm.ConfigurationAdmin; @@ -7,7 +8,6 @@ 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.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; @@ -20,8 +20,10 @@ import org.slf4j.LoggerFactory; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.OpenemsType; import io.openems.edge.battery.api.Battery; import io.openems.edge.battery.fenecon.home.statemachine.Context; import io.openems.edge.battery.fenecon.home.statemachine.StateMachine; @@ -30,9 +32,12 @@ import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.ModbusUtils; +import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; +import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.common.modbusslave.ModbusSlave; @@ -52,6 +57,13 @@ public class FeneconHomeBatteryImpl extends AbstractOpenemsModbusComponent implements OpenemsComponent, Battery, EventHandler, ModbusSlave, StartStoppable, FeneconHomeBattery { + private static final int TEMPERATURE_ADDRESS_OFFSET = 18; + private static final int VOLTAGE_ADDRESS_OFFSET = 2; + private static final int SENSORS_PER_MODULE = 14; + private static final int ADDRESS_OFFSET_FOR_CELL_VOLT_AND_TEMP = 100; + private static final int MODULE_MIN_VOLTAGE = 42; // [V] + private static final int MODULE_MAX_VOLTAGE = 45;// [V] + private final Logger log = LoggerFactory.getLogger(FeneconHomeBatteryImpl.class); @Reference @@ -82,14 +94,22 @@ protected void setModbus(BridgeModbus modbus) { @Activate void activate(ComponentContext context, Config config) throws OpenemsException { + this.config = config; + // Asynchronously read numberOfTowers and numberOfModulesPerTower + this.getNumberOfTowers().thenAccept(numberOfTowers -> { + this.getNumberOfModulesPerTowers().thenAccept(numberOfModulesPerTower -> { + int chargeMaxVoltageValue = numberOfModulesPerTower * MODULE_MAX_VOLTAGE; + // Set Battery Charge Max Voltage + this._setChargeMaxVoltage(chargeMaxVoltageValue); + // Set Battery Discharge Min Voltage + int minDischargeVoltageValue = numberOfModulesPerTower * MODULE_MIN_VOLTAGE; + this._setDischargeMinVoltage(minDischargeVoltageValue); + // Initialize available Tower- and Module-Channels dynamically. + this.initializeTowerModulesChannels(numberOfTowers, numberOfModulesPerTower); + }); + }); super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus", config.modbus_id()); - this.config = config; - } - - @Deactivate - protected void deactivate() { - super.deactivate(); } @Override @@ -213,47 +233,30 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { .bit(8, FeneconHomeBattery.ChannelId.FAULT_POSITION_BCU_9) // .bit(9, FeneconHomeBattery.ChannelId.FAULT_POSITION_BCU_10))// ), // + new FC3ReadRegistersTask(506, Priority.HIGH, // - m(new UnsignedWordElement(506)) // - .m(FeneconHomeBattery.ChannelId.BATTERY_RACK_VOLTAGE, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [mV] - .m(Battery.ChannelId.VOLTAGE, ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [V] - .build(), // - m(new UnsignedWordElement(507)) // - .m(FeneconHomeBattery.ChannelId.BATTERY_RACK_CURRENT, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [mV] - .m(Battery.ChannelId.CURRENT, ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [V] - .build(), - m(new UnsignedWordElement(508))// - .m(FeneconHomeBattery.ChannelId.BATTERY_RACK_SOC, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [%] - .m(Battery.ChannelId.SOC, ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [%] - .build(), // - m(new UnsignedWordElement(509)) // - .m(FeneconHomeBattery.ChannelId.BATTERY_RACK_SOH, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [%] - .m(Battery.ChannelId.SOH, ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [%] - .build(), // - m(FeneconHomeBattery.ChannelId.CELL_VOLTAGE_MIN, new UnsignedWordElement(510)), + m(Battery.ChannelId.VOLTAGE, new UnsignedWordElement(506), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [V] + m(Battery.ChannelId.CURRENT, new UnsignedWordElement(507), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [A] + m(Battery.ChannelId.SOC, new UnsignedWordElement(508), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [%] + m(Battery.ChannelId.SOH, new UnsignedWordElement(509), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [%] + m(Battery.ChannelId.MIN_CELL_VOLTAGE, new UnsignedWordElement(510)), // [mV] m(FeneconHomeBattery.ChannelId.ID_OF_CELL_VOLTAGE_MIN, new UnsignedWordElement(511)), // - m(FeneconHomeBattery.ChannelId.CELL_VOLTAGE_MAX, new UnsignedWordElement(512)), // + m(Battery.ChannelId.MAX_CELL_VOLTAGE, new UnsignedWordElement(512)), // [mV] m(FeneconHomeBattery.ChannelId.ID_OF_CELL_VOLTAGE_MAX, new UnsignedWordElement(513)), // - m(FeneconHomeBattery.ChannelId.MIN_TEMPERATURE, new UnsignedWordElement(514), // + m(FeneconHomeBattery.ChannelId.MIN_TEMPERATURE, new UnsignedWordElement(514), ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // m(FeneconHomeBattery.ChannelId.ID_OF_MIN_TEMPERATURE, new UnsignedWordElement(515)), // - m(FeneconHomeBattery.ChannelId.MAX_TEMPERATURE, new UnsignedWordElement(516)), // + m(FeneconHomeBattery.ChannelId.MAX_TEMPERATURE, new UnsignedWordElement(516), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // m(FeneconHomeBattery.ChannelId.ID_OF_MAX_TEMPERATURE, new UnsignedWordElement(517)), // - m(new UnsignedWordElement(518)) // - .m(FeneconHomeBattery.ChannelId.MAX_CHARGE_CURRENT, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // - .m(Battery.ChannelId.CHARGE_MAX_CURRENT, ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [%] - .build(), // - m(new UnsignedWordElement(519)) // - .m(FeneconHomeBattery.ChannelId.MAX_DISCHARGE_CURRENT, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) - .m(Battery.ChannelId.DISCHARGE_MAX_CURRENT, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [%] - .build(), // + m(Battery.ChannelId.CHARGE_MAX_CURRENT, new UnsignedWordElement(518), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [A] + m(Battery.ChannelId.DISCHARGE_MAX_CURRENT, new UnsignedWordElement(519), // + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [A] m(FeneconHomeBattery.ChannelId.MAX_DC_CHARGE_CURRENT_LIMIT_PER_BCU, new UnsignedWordElement(520), // ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // @@ -263,16 +266,8 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { m(FeneconHomeBattery.ChannelId.RACK_NUMBER_OF_BATTERY_BCU, new UnsignedWordElement(522)), // m(FeneconHomeBattery.ChannelId.RACK_NUMBER_OF_CELLS_IN_SERIES_PER_MODULE, new UnsignedWordElement(523)), // - m(new UnsignedWordElement(524)) // - .m(FeneconHomeBattery.ChannelId.RACK_MAX_CELL_VOLTAGE_LIMIT, - ElementToChannelConverter.DIRECT_1_TO_1) - .m(Battery.ChannelId.MAX_CELL_VOLTAGE, ElementToChannelConverter.DIRECT_1_TO_1) // [%] - .build(), // - m(new UnsignedWordElement(525)) // - .m(FeneconHomeBattery.ChannelId.RACK_MAX_CELL_VOLTAGE_LIMIT, - ElementToChannelConverter.DIRECT_1_TO_1) - .m(Battery.ChannelId.MAX_CELL_VOLTAGE, ElementToChannelConverter.DIRECT_1_TO_1) // [%] - .build(), // + m(FeneconHomeBattery.ChannelId.RACK_MAX_CELL_VOLTAGE_LIMIT, new UnsignedWordElement(524)), // + m(FeneconHomeBattery.ChannelId.RACK_MIN_CELL_VOLTAGE_LIMIT, new UnsignedWordElement(525)), // m(new BitsWordElement(526, this) // .bit(0, FeneconHomeBattery.ChannelId.RACK_HW_AFE_COMMUNICATION_FAULT) // .bit(1, FeneconHomeBattery.ChannelId.RACK_HW_ACTOR_DRIVER_FAULT) // @@ -294,111 +289,275 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { .bit(7, FeneconHomeBattery.ChannelId.RACK_SYSTEM_LOW_CELL_VOLTAGE_PERMANENT_FAILURE) // .bit(8, FeneconHomeBattery.ChannelId.RACK_SYSTEM_SHORT_CIRCUIT)), // m(FeneconHomeBattery.ChannelId.UPPER_VOLTAGE, new UnsignedWordElement(528))), // - new FC3ReadRegistersTask(10002, Priority.LOW, // - m(new BitsWordElement(10002, this) // - .bit(0, FeneconHomeBattery.ChannelId.BCU_STATUS_ALARM) // - .bit(1, FeneconHomeBattery.ChannelId.BCU_STATUS_WARNING) // - .bit(2, FeneconHomeBattery.ChannelId.BCU_STATUS_FAULT) // - .bit(3, FeneconHomeBattery.ChannelId.BCU_STATUS_PFET) // - .bit(4, FeneconHomeBattery.ChannelId.BCU_STATUS_CFET) // - .bit(5, FeneconHomeBattery.ChannelId.BCU_STATUS_DFET) // - .bit(6, FeneconHomeBattery.ChannelId.BCU_STATUS_BATTERY_IDLE) // - .bit(7, FeneconHomeBattery.ChannelId.BCU_STATUS_BATTERY_CHARGING) // - .bit(8, FeneconHomeBattery.ChannelId.BCU_STATUS_BATTERY_DISCHARGING)), // - m(new BitsWordElement(10003, this) // - .bit(0, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_CELL_OVER_VOLTAGE) // - .bit(1, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_CELL_UNDER_VOLTAGE) // - .bit(2, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_OVER_CHARGING_CURRENT) // - .bit(3, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_OVER_DISCHARGING_CURRENT) // - .bit(4, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_OVER_TEMPERATURE) // - .bit(5, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_UNDER_TEMPERATURE) // - .bit(6, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_CELL_VOLTAGE_DIFFERENCE) // - .bit(7, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_BCU_TEMP_DIFFERENCE) // - .bit(8, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_UNDER_SOC) // - .bit(9, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_UNDER_SOH) // - .bit(10, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_OVER_CHARGING_POWER) // - .bit(11, FeneconHomeBattery.ChannelId.BCU_PRE_ALARM_OVER_DISCHARGING_POWER) // - ), // - m(new BitsWordElement(10004, this) // - .bit(0, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_CELL_OVER_VOLTAGE) // - .bit(1, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_CELL_UNDER_VOLTAGE) // - .bit(2, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_OVER_CHARGING_CURRENT) // - .bit(3, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_OVER_DISCHARGING_CURRENT) // - .bit(4, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_OVER_TEMPERATURE) // - .bit(5, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_UNDER_TEMPERATURE) // - .bit(6, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_CELL_VOLTAGE_DIFFERENCE) // - .bit(7, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_BCU_TEMP_DIFFERENCE) // - .bit(8, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_UNDER_SOC) // - .bit(9, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_UNDER_SOH) // - .bit(10, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_OVER_CHARGING_POWER) // - .bit(11, FeneconHomeBattery.ChannelId.BCU_LEVEL_1_OVER_DISCHARGING_POWER) // - ), // - m(new BitsWordElement(10005, this) // - .bit(0, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_CELL_OVER_VOLTAGE) // - .bit(1, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_CELL_UNDER_VOLTAGE) // - .bit(2, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_OVER_CHARGING_CURRENT) // - .bit(3, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_OVER_DISCHARGING_CURRENT) // - .bit(4, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_OVER_TEMPERATURE) // - .bit(5, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_UNDER_TEMPERATURE) // - .bit(6, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_CELL_VOLTAGE_DIFFERENCE) // - .bit(7, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_BCU_TEMP_DIFFERENCE) // - .bit(8, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_TEMPERATURE_DIFFERENCE) // - .bit(9, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_INTERNAL_COMMUNICATION) // - .bit(10, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_EXTERNAL_COMMUNICATION) // - .bit(11, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_PRECHARGE_FAIL) // - .bit(12, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_PARALLEL_FAIL) // - .bit(13, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_SYSTEM_FAIL) // - .bit(14, FeneconHomeBattery.ChannelId.BCU_LEVEL_2_HARDWARE_FAIL)), // - m(new BitsWordElement(10006, this) // - .bit(0, FeneconHomeBattery.ChannelId.BCU_HW_AFE_COMMUNICAITON_FAULT) // - .bit(1, FeneconHomeBattery.ChannelId.BCU_HW_ACTOR_DRIVER_FAULT) // - .bit(2, FeneconHomeBattery.ChannelId.BCU_HW_EEPROM_COMMUNICATION_FAULT) // - .bit(3, FeneconHomeBattery.ChannelId.BCU_HW_VOLTAGE_DETECT_FAULT) // - .bit(4, FeneconHomeBattery.ChannelId.BCU_HW_TEMPERATURE_DETECT_FAULT) // - .bit(5, FeneconHomeBattery.ChannelId.BCU_HW_CURRENT_DETECT_FAULT) // - .bit(6, FeneconHomeBattery.ChannelId.BCU_HW_ACTOR_NOT_CLOSE) // - .bit(7, FeneconHomeBattery.ChannelId.BCU_HW_ACTOR_NOT_OPEN) // - .bit(8, FeneconHomeBattery.ChannelId.BCU_HW_FUSE_BROKEN)), // - m(new BitsWordElement(10007, this) // - .bit(0, FeneconHomeBattery.ChannelId.BCU_SYSTEM_AFE_OVER_TEMPERATURE) // - .bit(1, FeneconHomeBattery.ChannelId.BCU_SYSTEM_AFE_UNDER_TEMPERATURE) // - .bit(2, FeneconHomeBattery.ChannelId.BCU_SYSTEM_AFE_OVER_VOLTAGE) // - .bit(3, FeneconHomeBattery.ChannelId.BCU_SYSTEM_AFE_UNDER_VOLTAGE) // - .bit(4, FeneconHomeBattery.ChannelId.BCU_SYSTEM_HIGH_TEMPERATURE_PERMANENT_FAILURE) // - .bit(5, FeneconHomeBattery.ChannelId.BCU_SYSTEM_LOW_TEMPERATURE_PERMANENT_FAILURE) // - .bit(6, FeneconHomeBattery.ChannelId.BCU_SYSTEM_HIGH_CELL_VOLTAGE_PERMANENT_FAILURE) // - .bit(7, FeneconHomeBattery.ChannelId.BCU_SYSTEM_LOW_CELL_VOLTAGE_PERMANENT_FAILURE) // - .bit(8, FeneconHomeBattery.ChannelId.BCU_SYSTEM_SHORT_CIRCUIT)), // - m(FeneconHomeBattery.ChannelId.BCU_SOC, new UnsignedWordElement(10008), // [%] - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(FeneconHomeBattery.ChannelId.BCU_SOH, new UnsignedWordElement(10009), // [%] - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(FeneconHomeBattery.ChannelId.BCU_VOLTAGE, new UnsignedWordElement(10010), // [V] - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(FeneconHomeBattery.ChannelId.BCU_CURRENT, new UnsignedWordElement(10011), // [A] - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(FeneconHomeBattery.ChannelId.BCU_MIN_CELL_VOLTAGE, new UnsignedWordElement(10012)), // [mV] - m(FeneconHomeBattery.ChannelId.BCU_MAX_CELL_VOLTAGE, new UnsignedWordElement(10013)), // [mV] - m(FeneconHomeBattery.ChannelId.BCU_AVERAGE_CELL_VOLTAGE, new UnsignedWordElement(10014)), // - m(FeneconHomeBattery.ChannelId.BCU_MAX_CHARGE_CURRENT, new UnsignedWordElement(10015)), // - m(FeneconHomeBattery.ChannelId.BCU_MIN_CHARGE_CURRENT, new UnsignedWordElement(10016)), // - m(FeneconHomeBattery.ChannelId.BMS_SERIAL_NUMBER, new UnsignedWordElement(10017)), // - m(FeneconHomeBattery.ChannelId.NO_OF_CYCLES, new UnsignedWordElement(10018)), // - m(FeneconHomeBattery.ChannelId.DESIGN_CAPACITY, new UnsignedWordElement(10019), // - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [Ah] - m(new UnsignedWordElement(10020)) // - .m(FeneconHomeBattery.ChannelId.USABLE_CAPACITY, - ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [mV] - .m(Battery.ChannelId.CAPACITY, ElementToChannelConverter.DIRECT_1_TO_1) // [V] - .build(), // - m(FeneconHomeBattery.ChannelId.REMAINING_CAPACITY, new UnsignedWordElement(10021), // - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [Ah] - m(FeneconHomeBattery.ChannelId.BCU_MAX_CELL_VOLTAGE_LIMIT, new UnsignedWordElement(10022)), // - m(FeneconHomeBattery.ChannelId.BCU_MIN_CELL_VOLTAGE_LIMIT, new UnsignedWordElement(10023)), // - m(FeneconHomeBattery.ChannelId.BMU_NUMBER, new UnsignedWordElement(10024))), // new FC3ReadRegistersTask(44000, Priority.HIGH, // m(FeneconHomeBattery.ChannelId.BMS_CONTROL, new UnsignedWordElement(44000)) // - ));// + )); + } + + private void initializeTowerModulesChannels(int numberOfTowers, int numberOfModulePerTower) { + try { + for (int t = 1; t <= numberOfTowers; t++) { + final int towerOffset = (t - 1) * 2000 + 10000; + this.getModbusProtocol().addTasks(// + new FC3ReadRegistersTask(towerOffset + 2, Priority.LOW, // + m(new BitsWordElement(towerOffset + 2, this)// + .bit(0, this.generateTowerChannel(t, "STATUS_ALARM")) // + .bit(1, this.generateTowerChannel(t, "STATUS_WARNING")) // + .bit(2, this.generateTowerChannel(t, "STATUS_FAULT")) // + .bit(3, this.generateTowerChannel(t, "STATUS_PFET")) // + .bit(4, this.generateTowerChannel(t, "STATUS_CFET")) // + .bit(5, this.generateTowerChannel(t, "STATUS_DFET")) // + .bit(6, this.generateTowerChannel(t, "STATUS_BATTERY_IDLE")) // + .bit(7, this.generateTowerChannel(t, "STATUS_BATTERY_CHARGING")) // + .bit(8, this.generateTowerChannel(t, "STATUS_BATTERY_DISCHARGING"))// + ), // + m(new BitsWordElement(towerOffset + 3, this) + .bit(0, this.generateTowerChannel(t, "PRE_ALARM_CELL_OVER_VOLTAGE")) // + .bit(1, this.generateTowerChannel(t, "PRE_ALARM_CELL_UNDER_VOLTAGE")) // + .bit(2, this.generateTowerChannel(t, "PRE_ALARM_OVER_CHARGING_CURRENT")) // + .bit(3, this.generateTowerChannel(t, "PRE_ALARM_OVER_DISCHARGING_CURRENT")) // + .bit(4, this.generateTowerChannel(t, "PRE_ALARM_OVER_TEMPERATURE")) // + .bit(5, this.generateTowerChannel(t, "PRE_ALARM_UNDER_TEMPERATURE")) // + .bit(6, this.generateTowerChannel(t, "PRE_ALARM_CELL_VOLTAGE_DIFFERENCE")) // + .bit(7, this.generateTowerChannel(t, "PRE_ALARM_BCU_TEMP_DIFFERENCE")) // + .bit(8, this.generateTowerChannel(t, "PRE_ALARM_UNDER_SOC")) // + .bit(9, this.generateTowerChannel(t, "PRE_ALARM_UNDER_SOH")) // + .bit(10, this.generateTowerChannel(t, "PRE_ALARM_OVER_CHARGING_POWER")) // + .bit(11, this.generateTowerChannel(t, "PRE_ALARM_OVER_DISCHARGING_POWER"))), // + m(new BitsWordElement(towerOffset + 4, this) + .bit(0, this.generateTowerChannel(t, "LEVEL_1_CELL_OVER_VOLTAGE")) // + .bit(1, this.generateTowerChannel(t, "LEVEL_1_CELL_UNDER_VOLTAGE")) // + .bit(2, this.generateTowerChannel(t, "LEVEL_1_OVER_CHARGING_CURRENT")) // + .bit(3, this.generateTowerChannel(t, "LEVEL_1_OVER_DISCHARGING_CURRENT")) // + .bit(4, this.generateTowerChannel(t, "LEVEL_1_OVER_TEMPERATURE")) // + .bit(5, this.generateTowerChannel(t, "LEVEL_1_UNDER_TEMPERATURE")) // + .bit(6, this.generateTowerChannel(t, "LEVEL_1_CELL_VOLTAGE_DIFFERENCE")) // + .bit(7, this.generateTowerChannel(t, "LEVEL_1_BCU_TEMP_DIFFERENCE")) // + .bit(8, this.generateTowerChannel(t, "LEVEL_1_UNDER_SOC")) // + .bit(9, this.generateTowerChannel(t, "LEVEL_1_UNDER_SOH")) // + .bit(10, this.generateTowerChannel(t, "LEVEL_1_OVER_CHARGING_POWER")) // + .bit(11, this.generateTowerChannel(t, "LEVEL_1_OVER_DISCHARGING_POWER"))), + m(new BitsWordElement(towerOffset + 5, this) + .bit(0, this.generateTowerChannel(t, "LEVEL_2_CELL_OVER_VOLTAGE")) // + .bit(1, this.generateTowerChannel(t, "LEVEL_2_CELL_UNDER_VOLTAGE")) // + .bit(2, this.generateTowerChannel(t, "LEVEL_2_OVER_CHARGING_CURRENT")) // + .bit(3, this.generateTowerChannel(t, "LEVEL_2_OVER_DISCHARGING_CURRENT")) // + .bit(4, this.generateTowerChannel(t, "LEVEL_2_OVER_TEMPERATURE")) // + .bit(5, this.generateTowerChannel(t, "LEVEL_2_UNDER_TEMPERATURE")) // + .bit(6, this.generateTowerChannel(t, "LEVEL_2_CELL_VOLTAGE_DIFFERENCE")) // + .bit(7, this.generateTowerChannel(t, "LEVEL_2_BCU_TEMP_DIFFERENCE")) // + .bit(8, this.generateTowerChannel(t, "LEVEL_2_TEMPERATURE_DIFFERENCE")) // + .bit(9, this.generateTowerChannel(t, "LEVEL_2_INTERNAL_COMMUNICATION")) // + .bit(10, this.generateTowerChannel(t, "LEVEL_2_EXTERNAL_COMMUNICATION")) // + .bit(11, this.generateTowerChannel(t, "LEVEL_2_PRECHARGE_FAIL")) // + .bit(12, this.generateTowerChannel(t, "LEVEL_2_PARALLEL_FAIL")) // + .bit(13, this.generateTowerChannel(t, "LEVEL_2_SYSTEM_FAIL")) // + .bit(14, this.generateTowerChannel(t, "LEVEL_2_HARDWARE_FAIL"))), // + m(new BitsWordElement(towerOffset + 6, this) + .bit(0, this.generateTowerChannel(t, "HW_AFE_COMMUNICAITON_FAULT")) // + .bit(1, this.generateTowerChannel(t, "HW_ACTOR_DRIVER_FAULT")) // + .bit(2, this.generateTowerChannel(t, "HW_EEPROM_COMMUNICATION_FAULT")) // + .bit(3, this.generateTowerChannel(t, "HW_VOLTAGE_DETECT_FAULT")) // + .bit(4, this.generateTowerChannel(t, "HW_TEMPERATURE_DETECT_FAULT")) // + .bit(5, this.generateTowerChannel(t, "HW_CURRENT_DETECT_FAULT")) // + .bit(6, this.generateTowerChannel(t, "HW_ACTOR_NOT_CLOSE")) // + .bit(7, this.generateTowerChannel(t, "HW_ACTOR_NOT_OPEN")) // + .bit(8, this.generateTowerChannel(t, "HW_FUSE_BROKEN"))), // + m(new BitsWordElement(towerOffset + 7, this) + .bit(0, this.generateTowerChannel(t, "SYSTEM_AFE_OVER_TEMPERATURE")) // + .bit(1, this.generateTowerChannel(t, "SYSTEM_AFE_UNDER_TEMPERATURE")) // + .bit(2, this.generateTowerChannel(t, "SYSTEM_AFE_OVER_VOLTAGE")) // + .bit(3, this.generateTowerChannel(t, "SYSTEM_AFE_UNDER_VOLTAGE")) // + .bit(4, this.generateTowerChannel(t, + "SYSTEM_HIGH_TEMPERATURE_PERMANENT_FAILURE")) // + .bit(5, this.generateTowerChannel(t, + "SYSTEM_LOW_TEMPERATURE_PERMANENT_FAILURE")) // + .bit(6, this.generateTowerChannel(t, + "SYSTEM_HIGH_CELL_VOLTAGE_PERMANENT_FAILURE")) // + .bit(7, this.generateTowerChannel(t, + "SYSTEM_LOW_CELL_VOLTAGE_PERMANENT_FAILURE")) // + .bit(8, this.generateTowerChannel(t, "SYSTEM_SHORT_CIRCUIT"))), // + m(this.generateTowerChannel(t, "_SOC"), new UnsignedWordElement(towerOffset + 8), // [%] + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(this.generateTowerChannel(t, "_SOH"), new UnsignedWordElement(towerOffset + 9), // [%] + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(this.generateTowerChannel(t, "_VOLTAGE"), new UnsignedWordElement(towerOffset + 10), // [V] + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(this.generateTowerChannel(t, "_CURRENT"), new UnsignedWordElement(towerOffset + 11), // [A] + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(this.generateTowerChannel(t, "_MIN_CELL_VOLTAGE"), + new UnsignedWordElement(towerOffset + 12)), // [mV] + m(this.generateTowerChannel(t, "_MAX_CELL_VOLTAGE"), + new UnsignedWordElement(towerOffset + 13)), // [mV] + m(this.generateTowerChannel(t, "_AVARAGE_CELL_VOLTAGE"), + new UnsignedWordElement(towerOffset + 14)), // + m(this.generateTowerChannel(t, "_MAX_CHARGE_CURRENT"), + new UnsignedWordElement(towerOffset + 15)), // + m(this.generateTowerChannel(t, "_MIN_CHARGE_CURRENT"), + new UnsignedWordElement(towerOffset + 16)), // + m(this.generateTowerChannel(t, "_BMS_SERIAL_NUMBER"), + new UnsignedWordElement(towerOffset + 17)), // + m(this.generateTowerChannel(t, "_NO_OF_CYCLES"), + new UnsignedWordElement(towerOffset + 18)), // + m(new UnsignedWordElement(towerOffset + 19)) // + .m(this.generateTowerChannel(t, "_DESIGN_CAPACITY"), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // [Wh] + .m(Battery.ChannelId.CAPACITY, ElementToChannelConverter.DIRECT_1_TO_1) // + .build(), // + m(this.generateTowerChannel(t, "_USABLE_CAPACITY"), + new UnsignedWordElement(towerOffset + 20), // + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [Wh] + m(this.generateTowerChannel(t, "_REMAINING_CAPACITY"), + new UnsignedWordElement(towerOffset + 21), // + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // [Wh] + m(this.generateTowerChannel(t, "_MAX_CELL_VOLTAGE_LIMIT"), + new UnsignedWordElement(towerOffset + 22)), // + m(this.generateTowerChannel(t, "_MIN_CELL_VOLTAGE_LIMIT"), + new UnsignedWordElement(towerOffset + 23)))); + + /* + * Dynamically generate Channels and Modbus mappings for Cell-Temperatures and + * for Cell-Voltages.Channel-IDs are like "TOWER_1_OFFSET_2_TEMPERATURE_003". + * Channel-IDs are like "TOWER_1_OFFSET_2_VOLTAGE_003". + */ + for (int i = 1; i < numberOfModulePerTower + 1; i++) { + AbstractModbusElement[] ameVolt = new AbstractModbusElement[SENSORS_PER_MODULE]; + AbstractModbusElement[] ameTemp = new AbstractModbusElement[SENSORS_PER_MODULE]; + for (int j = 0; j < SENSORS_PER_MODULE; j++) { + { + // Create Voltage Channel + ChannelIdImpl channelId = new ChannelIdImpl(// + this.getSingleCellPrefix(t, i, j) + "_VOLTAGE", + Doc.of(OpenemsType.INTEGER).unit(Unit.VOLT)); + this.addChannel(channelId); + // Create Modbus-Mapping for Voltages + UnsignedWordElement uwe = new UnsignedWordElement(towerOffset + + i * ADDRESS_OFFSET_FOR_CELL_VOLT_AND_TEMP + VOLTAGE_ADDRESS_OFFSET + j); + ameVolt[j] = m(channelId, uwe); + } + { + // Create Temperature Channel + ChannelIdImpl channelId = new ChannelIdImpl(// + this.getSingleCellPrefix(t, i, j) + "_TEMPERATURE", + Doc.of(OpenemsType.INTEGER).unit(Unit.DEZIDEGREE_CELSIUS)); + this.addChannel(channelId); + + // Create Modbus-Mapping for Temperatures + // Cell Temperatures Read Registers for Tower_1 starts from 10000, for Tower_2 + // 12000, for Tower_3 14000 + // (t-1)*2000+10000) calculates Tower Offset value + UnsignedWordElement uwe = new UnsignedWordElement(towerOffset + + i * ADDRESS_OFFSET_FOR_CELL_VOLT_AND_TEMP + TEMPERATURE_ADDRESS_OFFSET + j); + ameTemp[j] = m(channelId, uwe); + } + } + this.getModbusProtocol().addTasks(// + new FC3ReadRegistersTask( + towerOffset + ADDRESS_OFFSET_FOR_CELL_VOLT_AND_TEMP * i + VOLTAGE_ADDRESS_OFFSET, + Priority.LOW, ameVolt), // + new FC3ReadRegistersTask(// + towerOffset + ADDRESS_OFFSET_FOR_CELL_VOLT_AND_TEMP * i + + TEMPERATURE_ADDRESS_OFFSET, + Priority.LOW, ameTemp)); + } + } + } catch (OpenemsException e) { + e.printStackTrace(); + } + } + + /** + * Gets the Number of Modules Per Tower. + * + * @return the Number of Modules Per Tower as a {@link CompletableFuture}. + * @throws OpenemsException on error + */ + private CompletableFuture getNumberOfModulesPerTowers() { + final CompletableFuture result = new CompletableFuture(); + try { + ModbusUtils.readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(10024), true) + .thenAccept(numberOfModulesPerTower -> { + if (numberOfModulesPerTower == null) { + return; + } + result.complete(numberOfModulesPerTower); + }); + } catch (OpenemsException e) { + result.completeExceptionally(e); + } + return result; + } + + /** + * Gets the Number of Towers. + * + * @return the Number of Towers as a {@link CompletableFuture}. + */ + private CompletableFuture getNumberOfTowers() { + final CompletableFuture result = new CompletableFuture(); + try { + ModbusUtils.readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(14000), true) + .thenAccept(softwareVersionOfTower3 -> { + if (softwareVersionOfTower3 == null) { + return; + } + if (softwareVersionOfTower3 != 0) { + // Three Towers available + result.complete(3); + } else { + try { + ModbusUtils + .readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(12000), true) + .thenAccept(softwareVersionOfTower2 -> { + if (softwareVersionOfTower2 == null) { + return; + } + if (softwareVersionOfTower2 != 0) { + // Two Towers available + result.complete(2); + } else { + // One Tower available + result.complete(1); + } + }); + } catch (OpenemsException e) { + result.completeExceptionally(e); + } + } + }); + } catch (OpenemsException e) { + result.completeExceptionally(e); + } + return result; + } + + /** + * Generates prefix for Channel-IDs for Cell Temperature and Voltage channels. + * + *

    + * "%03d" creates string number with leading zeros + * + * @param num number of the Cell + * @param module number of the Module + * @param tower number of the Tower + * @return a prefix e.g. "TOWER_1_MODULE_2_CELL_003" + */ + private String getSingleCellPrefix(int tower, int module, int num) { + return "TOWER_" + tower + "_MODULE_" + module + "_CELL_" + String.format("%03d", num); + } + + /** + * Generates a Channel-ID for channels that are specific to a tower. + * + * @param tower number of the Tower + * @param channelIdSuffix e.g. "STATUS_ALARM" + * @return a channel with Channel-ID "TOWER_1_STATUS_ALARM" + */ + private ChannelIdImpl generateTowerChannel(int tower, String channelIdSuffix) { + ChannelIdImpl channelId = new ChannelIdImpl("TOWER_" + tower + "_" + channelIdSuffix, + Doc.of(OpenemsType.BOOLEAN)); + this.addChannel(channelId); + return channelId; } @Override diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/enums/BmsControl.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/enums/BmsControl.java index 818a6875455..896c66ef1e8 100644 --- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/enums/BmsControl.java +++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/enums/BmsControl.java @@ -4,8 +4,11 @@ public enum BmsControl implements OptionsEnum { UNDEFINED(-1, "Undefined"), // + SWITCHED_ON(0, "Switch ON Pre-charge & Main Power Contactor"), // SWITCHED_OFF(0x1, "Shut Down Main Power Contactor & Pre-charge"), // - SWITCHED_ON(0, "Switch ON Pre-charge & Main Power Contactor"); + // 0x2 is not documented in the modbus protocol, but we still read it from time + // to time. + IGNORED(0x2, "Switch ON Pre-charge & Main Power Contactor"); private final int value; private final String name; diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/enums/State.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/enums/State.java deleted file mode 100644 index 0b75a100f76..00000000000 --- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/enums/State.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.openems.edge.battery.fenecon.home.enums; - -import io.openems.common.types.OptionsEnum; - -public enum State implements OptionsEnum { - UNDEFINED("Undefined", -1), // - PENDING("Pending", 0), // - OFF("Off", 1), // - INIT("Initializing", 2), // - RUNNING("Running", 3), // - STOPPING("Stopping", 4), // - ERROR("Error", 5), // - ERRORDELAY("Errordelay", 6), // - ERROR_HANDLING("Errordelay", 7), // - ; - - private State(String name, int value) { - this.name = name; - this.value = value; - } - - private int value; - private String name; - - @Override - public int getValue() { - return this.value; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public OptionsEnum getUndefined() { - return UNDEFINED; - } - -} diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoRunningHandler.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoRunningHandler.java index e9ddd1ffdf1..9c9cb5e8759 100644 --- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoRunningHandler.java +++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoRunningHandler.java @@ -14,7 +14,7 @@ public State runAndGetNextState(Context context) throws OpenemsNamedException { BmsControl bmsControl = battery.getBmsControl(); // We can no nothing but wait... - if (bmsControl == BmsControl.SWITCHED_ON) { + if (bmsControl == BmsControl.SWITCHED_ON || bmsControl == BmsControl.IGNORED) { return State.RUNNING; } else { diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/RunningHandler.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/RunningHandler.java index 628b7d7808f..70ec62c7b61 100644 --- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/RunningHandler.java +++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/RunningHandler.java @@ -16,7 +16,8 @@ public State runAndGetNextState(Context context) { return State.UNDEFINED; } - if (battery.getBmsControl() != BmsControl.SWITCHED_ON) { + if (battery.getBmsControl() != BmsControl.SWITCHED_ON + && battery.getBmsControl() != BmsControl.IGNORED) { return State.UNDEFINED; } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryProtectionDefinitionSoltaro.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryProtectionDefinitionSoltaro.java index a282ed61bba..a3c236f8703 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryProtectionDefinitionSoltaro.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/BatteryProtectionDefinitionSoltaro.java @@ -34,8 +34,8 @@ public PolyLine getChargeVoltageToPercent() { public PolyLine getDischargeVoltageToPercent() { return PolyLine.create() // .addPoint(2900, 0) // - .addPoint(Math.nextUp(2900), 0.05) // - .addPoint(2920, 0.05) // + .addPoint(Math.nextUp(2900), 0.01) // + .addPoint(2920, 0.01) // .addPoint(3000, 1) // .addPoint(3700, 1) // .addPoint(Math.nextUp(3700), 0) // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/SoltaroCellCharacteristic.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/SoltaroCellCharacteristic.java deleted file mode 100644 index adaba722950..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/SoltaroCellCharacteristic.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.openems.edge.battery.soltaro; - -import io.openems.edge.battery.api.CellCharacteristic; - -public class SoltaroCellCharacteristic implements CellCharacteristic { - - @Override - public int getFinalCellDischargeVoltage_mV() { - return 2_900; // - } - - @Override - public int getForceChargeCellVoltage_mV() { - return 2_850; // 0x2046 Cell under voltage Protection + 50 mV (i.e. x2046 ==> 2800) - } - - @Override - public int getFinalCellChargeVoltage_mV() { - return 3_650; // 0x0041 Cell Over Voltage Recover / 0x0080 Cell over Voltage Alarm - } - - @Override - public int getForceDischargeCellVoltage_mV() { - return 3_680; // - } - -} \ No newline at end of file diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/ClusterSettings.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/ClusterSettings.java deleted file mode 100644 index 7076b86331f..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/ClusterSettings.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.openems.edge.battery.soltaro.cluster; - -import io.openems.edge.battery.api.Settings; - -public class ClusterSettings implements Settings { - - private static final double POWER_FACTOR = 0.02; - private static final int MINIMUM_CURRENT_AMPERE = 1; - private static final int TOLERANCE_MILLI_VOLT = 10; - private static final int MAX_INCREASE_MILLIAMPERE = 300; - - private int numberOfUsedRacks = 1; - - /** - * Sets the number of used racks. - * @param numberOfUsedRacks int - */ - public void setNumberOfUsedRacks(int numberOfUsedRacks) { - this.numberOfUsedRacks = numberOfUsedRacks; - } - - @Override - public int getMaxIncreaseMilliAmpere() { - return MAX_INCREASE_MILLIAMPERE * this.numberOfUsedRacks; - } - - @Override - public double getPowerFactor() { - return POWER_FACTOR; - } - - @Override - public double getMinimumCurrentAmpere() { - return MINIMUM_CURRENT_AMPERE * this.numberOfUsedRacks; - } - - @Override - public int getToleranceMilliVolt() { - return TOLERANCE_MILLI_VOLT; - } - -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/SoltaroCluster.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/SoltaroCluster.java index 657668b9f55..af8e314bb96 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/SoltaroCluster.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/SoltaroCluster.java @@ -89,11 +89,7 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId .unit(Unit.MILLIAMPERE)), // SYSTEM_INSULATION(Doc.of(OpenemsType.INTEGER) // .unit(Unit.OHM)), // - CLUSTER_MAX_ALLOWED_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // - CLUSTER_MAX_ALLOWED_DISCHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // - + /* * StateChannels */ diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/ClusterVersionB.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/ClusterVersionB.java index 1a3d6c6d33f..9601b26086c 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/ClusterVersionB.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/ClusterVersionB.java @@ -26,13 +26,12 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.battery.api.Battery; -import io.openems.edge.battery.api.SetAllowedCurrents; +import io.openems.edge.battery.protection.BatteryProtection; +import io.openems.edge.battery.soltaro.BatteryProtectionDefinitionSoltaro; import io.openems.edge.battery.soltaro.BatteryState; import io.openems.edge.battery.soltaro.ModuleParameters; import io.openems.edge.battery.soltaro.ResetState; -import io.openems.edge.battery.soltaro.SoltaroCellCharacteristic; import io.openems.edge.battery.soltaro.State; -import io.openems.edge.battery.soltaro.cluster.ClusterSettings; import io.openems.edge.battery.soltaro.cluster.SoltaroCluster; import io.openems.edge.battery.soltaro.cluster.enums.ClusterStartStop; import io.openems.edge.battery.soltaro.cluster.enums.RackUsage; @@ -52,6 +51,7 @@ import io.openems.edge.common.channel.EnumWriteChannel; import io.openems.edge.common.channel.IntegerWriteChannel; import io.openems.edge.common.channel.StateChannel; +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.modbusslave.ModbusSlave; @@ -72,9 +72,6 @@ public class ClusterVersionB extends AbstractOpenemsModbusComponent implements SoltaroCluster, Battery, OpenemsComponent, EventHandler, ModbusSlave, StartStoppable { - public static final int DISCHARGE_MAX_A = 0; // default value 0 to avoid damages - public static final int CHARGE_MAX_A = 0; // default value 0 to avoid damages - private static final int ADDRESS_OFFSET_RACK_1 = 0x2000; private static final int ADDRESS_OFFSET_RACK_2 = 0x3000; private static final int ADDRESS_OFFSET_RACK_3 = 0x4000; @@ -86,10 +83,13 @@ public class ClusterVersionB extends AbstractOpenemsModbusComponent // are used or not private static final Map RACK_INFO = createRackInfo(); private final Logger log = LoggerFactory.getLogger(ClusterVersionB.class); - private final SetAllowedCurrents setAllowedCurrents; + @Reference protected ConfigurationAdmin cm; + @Reference + protected ComponentManager componentManager; + // If an error has occurred, this indicates the time when next action could be // done private LocalDateTime errorDelayIsOver = null; @@ -106,7 +106,7 @@ public class ClusterVersionB extends AbstractOpenemsModbusComponent private ResetState resetState = ResetState.NONE; private boolean resetDone; - private ClusterSettings clusterSettings = new ClusterSettings(); + private BatteryProtection batteryProtection = null; public ClusterVersionB() { super(// @@ -114,14 +114,8 @@ public ClusterVersionB() { Battery.ChannelId.values(), // StartStoppable.ChannelId.values(), // SoltaroCluster.ChannelId.values(), // - ClusterVersionBChannelId.values() // - ); - this.setAllowedCurrents = new SetAllowedCurrents(// - this, // - new SoltaroCellCharacteristic(), // - this.clusterSettings, // - this.channel(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_CHARGE_CURRENT), // - this.channel(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_DISCHARGE_CURRENT) // + ClusterVersionBChannelId.values(), // + BatteryProtection.ChannelId.values() // ); } @@ -151,25 +145,17 @@ void activate(ComponentContext context, Config config) throws OpenemsException { this.modbusBridgeId = config.modbus_id(); this.batteryState = config.batteryState(); - this._setChargeMaxCurrent(ClusterVersionB.CHARGE_MAX_A); - this._setDischargeMaxCurrent(ClusterVersionB.DISCHARGE_MAX_A); + // Initialize Battery-Protection + this.batteryProtection = BatteryProtection.create(this) // + .applyBatteryProtectionDefinition(new BatteryProtectionDefinitionSoltaro(), this.componentManager) // + .build(); + this._setChargeMaxVoltage( this.config.numberOfSlaves() * ModuleParameters.MAX_VOLTAGE_MILLIVOLT.getValue() / 1000); this._setDischargeMinVoltage( this.config.numberOfSlaves() * ModuleParameters.MIN_VOLTAGE_MILLIVOLT.getValue() / 1000); this._setCapacity( this.config.racks().length * this.config.numberOfSlaves() * this.config.moduleType().getCapacity_Wh()); - - this.clusterSettings.setNumberOfUsedRacks(calculateUsedRacks(config)); - } - - private static int calculateUsedRacks(Config conf) { - int num = 0; - if (conf.racks() != null) { - num = conf.racks().length; - } - - return num; } @Override @@ -181,10 +167,9 @@ public void handleEvent(Event event) { switch (event.getTopic()) { case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE: - - this.setAllowedCurrents.act(); - + this.batteryProtection.apply(); break; + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: this.handleBatteryState(); break; @@ -615,10 +600,10 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { new FC3ReadRegistersTask(0x104A, Priority.HIGH, // m(SoltaroCluster.ChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x104A)), // new DummyRegisterElement(0x104B, 0x104D), // - m(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_CHARGE_CURRENT, new UnsignedWordElement(0x104E), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_DISCHARGE_CURRENT, - new UnsignedWordElement(0x104F), ElementToChannelConverter.SCALE_FACTOR_2) // + m(BatteryProtection.ChannelId.BP_CHARGE_BMS, new UnsignedWordElement(0x104E), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(BatteryProtection.ChannelId.BP_DISCHARGE_BMS, new UnsignedWordElement(0x104F), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC3ReadRegistersTask(0x1081, Priority.LOW, // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/ClusterVersionCImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/ClusterVersionCImpl.java index 40c13b38473..498fdfc01d1 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/ClusterVersionCImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/ClusterVersionCImpl.java @@ -25,9 +25,8 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.battery.api.Battery; -import io.openems.edge.battery.api.SetAllowedCurrents; -import io.openems.edge.battery.soltaro.SoltaroCellCharacteristic; -import io.openems.edge.battery.soltaro.cluster.ClusterSettings; +import io.openems.edge.battery.protection.BatteryProtection; +import io.openems.edge.battery.soltaro.BatteryProtectionDefinitionSoltaro; import io.openems.edge.battery.soltaro.cluster.SoltaroCluster; import io.openems.edge.battery.soltaro.cluster.enums.Rack; import io.openems.edge.battery.soltaro.cluster.versionc.statemachine.Context; @@ -52,6 +51,7 @@ import io.openems.edge.common.channel.EnumReadChannel; import io.openems.edge.common.channel.IntegerReadChannel; import io.openems.edge.common.channel.IntegerWriteChannel; +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.modbusslave.ModbusSlave; @@ -77,18 +77,18 @@ public class ClusterVersionCImpl extends AbstractOpenemsModbusComponent implemen @Reference protected ConfigurationAdmin cm; - + + @Reference + protected ComponentManager componentManager; /** * Manages the {@link State}s of the StateMachine. */ private final StateMachine stateMachine = new StateMachine(State.UNDEFINED); - private final SetAllowedCurrents setAllowedCurrents; private Config config; private Set racks = new HashSet<>(); - private ClusterSettings clusterSettings = new ClusterSettings(); - + private BatteryProtection batteryProtection = null; public ClusterVersionCImpl() { super(// @@ -97,16 +97,10 @@ public ClusterVersionCImpl() { SoltaroBatteryVersionC.ChannelId.values(), // SoltaroCluster.ChannelId.values(), // StartStoppable.ChannelId.values(), // - ClusterVersionC.ChannelId.values() // + ClusterVersionC.ChannelId.values(), // + BatteryProtection.ChannelId.values() // ); - - this.setAllowedCurrents = new SetAllowedCurrents(// - this, // - new SoltaroCellCharacteristic(), // - this.clusterSettings, // - this.channel(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_CHARGE_CURRENT), // - this.channel(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_DISCHARGE_CURRENT) // - ); + } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) @@ -139,6 +133,11 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept return; } + // Initialize Battery-Protection + this.batteryProtection = BatteryProtection.create(this) // + .applyBatteryProtectionDefinition(new BatteryProtectionDefinitionSoltaro(), this.componentManager) // + .build(); + // Calculate Capacity int capacity = this.config.numberOfSlaves() * this.config.moduleType().getCapacity_Wh(); this._setCapacity(capacity); @@ -152,8 +151,6 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept this._setDischargeMaxCurrent(0 /* default value 0 to avoid damages */); this._setChargeMaxVoltage(this.config.numberOfSlaves() * Constants.MAX_VOLTAGE_MILLIVOLT / 1000); this._setDischargeMinVoltage(this.config.numberOfSlaves() * Constants.MIN_VOLTAGE_MILLIVOLT / 1000); - - this.clusterSettings.setNumberOfUsedRacks(calculateUsedRacks(config)); } @Override @@ -165,7 +162,7 @@ public void handleEvent(Event event) { case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE: this.updateChannels(); - this.setAllowedCurrents.act(); + this.batteryProtection.apply(); break; case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: @@ -252,10 +249,10 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // m(SoltaroCluster.ChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x104A)), // new DummyRegisterElement(0x104B, 0x104D), // - m(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_CHARGE_CURRENT, new UnsignedWordElement(0x104E), - ElementToChannelConverter.SCALE_FACTOR_2), // - m(SoltaroCluster.ChannelId.CLUSTER_MAX_ALLOWED_DISCHARGE_CURRENT, new UnsignedWordElement(0x104F), - ElementToChannelConverter.SCALE_FACTOR_2)), // + m(BatteryProtection.ChannelId.BP_CHARGE_BMS, new UnsignedWordElement(0x104E), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(BatteryProtection.ChannelId.BP_DISCHARGE_BMS, new UnsignedWordElement(0x104F), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // new FC3ReadRegistersTask(0x1081, Priority.LOW, // m(new BitsWordElement(0x1081, this) // .bit(0, ClusterVersionC.ChannelId.MASTER_PCS_COMMUNICATION_FAILURE) // @@ -431,12 +428,12 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { m(this.rack(r, RackChannel.MIN_CELL_VOLTAGE), new UnsignedWordElement(r.offset + 0x108)), m(this.rack(r, RackChannel.MAX_CELL_TEMPERATURE_ID), new UnsignedWordElement(r.offset + 0x109)), - m(this.rack(r, RackChannel.MAX_CELL_TEMPERATURE), - new SignedWordElement(r.offset + 0x10A)), + m(this.rack(r, RackChannel.MAX_CELL_TEMPERATURE), new SignedWordElement(r.offset + 0x10A), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), m(this.rack(r, RackChannel.MIN_CELL_TEMPERATURE_ID), new UnsignedWordElement(r.offset + 0x10B)), - m(this.rack(r, RackChannel.MIN_CELL_TEMPERATURE), - new SignedWordElement(r.offset + 0x10C)), + m(this.rack(r, RackChannel.MIN_CELL_TEMPERATURE), new SignedWordElement(r.offset + 0x10C), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), m(this.rack(r, RackChannel.AVERAGE_VOLTAGE), new UnsignedWordElement(r.offset + 0x10D)), m(this.rack(r, RackChannel.SYSTEM_INSULATION), new UnsignedWordElement(r.offset + 0x10E)), m(this.rack(r, RackChannel.SYSTEM_MAX_CHARGE_CURRENT), @@ -965,24 +962,4 @@ public StartStop getStartStopTarget() { assert false; return StartStop.UNDEFINED; // can never happen } - - private static int calculateUsedRacks(Config conf) { - int num = 0; - if (conf.isRack1Used()) { - num = num + 1; - } - if (conf.isRack2Used()) { - num = num + 1; - } - if (conf.isRack3Used()) { - num = num + 1; - } - if (conf.isRack4Used()) { - num = num + 1; - } - if (conf.isRack5Used()) { - num = num + 1; - } - return num; - } } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/SingleRackSettings.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/SingleRackSettings.java deleted file mode 100644 index c9c2b01163a..00000000000 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/SingleRackSettings.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.openems.edge.battery.soltaro.single; - -import io.openems.edge.battery.api.Settings; - -public class SingleRackSettings implements Settings { - - private static final double POWER_FACTOR = 0.02; - private static final int MINIMUM_CURRENT_AMPERE = 1; - private static final int TOLERANCE_MILLI_VOLT = 10; - private static final int MAX_INCREASE_MILLIAMPERE = 300; - - @Override - public int getMaxIncreaseMilliAmpere() { - return MAX_INCREASE_MILLIAMPERE; - } - - @Override - public double getPowerFactor() { - return POWER_FACTOR; - } - - @Override - public double getMinimumCurrentAmpere() { - return MINIMUM_CURRENT_AMPERE; - } - - @Override - public int getToleranceMilliVolt() { - return TOLERANCE_MILLI_VOLT; - } - -} diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/SingleRack.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/SingleRack.java index 5e536f83d53..ad3483d80a2 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/SingleRack.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/SingleRack.java @@ -28,12 +28,11 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.OpenemsType; import io.openems.edge.battery.api.Battery; -import io.openems.edge.battery.api.SetAllowedCurrents; +import io.openems.edge.battery.protection.BatteryProtection; +import io.openems.edge.battery.soltaro.BatteryProtectionDefinitionSoltaro; import io.openems.edge.battery.soltaro.BatteryState; import io.openems.edge.battery.soltaro.ChargeIndication; -import io.openems.edge.battery.soltaro.SoltaroCellCharacteristic; import io.openems.edge.battery.soltaro.State; -import io.openems.edge.battery.soltaro.single.SingleRackSettings; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; @@ -48,6 +47,7 @@ import io.openems.edge.common.channel.EnumReadChannel; import io.openems.edge.common.channel.EnumWriteChannel; import io.openems.edge.common.channel.StateChannel; +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.modbusslave.ModbusSlave; @@ -71,9 +71,6 @@ public class SingleRack extends AbstractOpenemsModbusComponent // Default values for the battery ranges public static final int DISCHARGE_MIN_V = 696; public static final int CHARGE_MAX_V = 854; - public static final int DISCHARGE_MAX_A = 0; - public static final int CHARGE_MAX_A = 0; - protected static final int SYSTEM_ON = 1; protected static final int SYSTEM_OFF = 0; @@ -88,6 +85,9 @@ public class SingleRack extends AbstractOpenemsModbusComponent @Reference protected ConfigurationAdmin cm; + @Reference + protected ComponentManager componentManager; + // If an error has occurred, this indicates the time when next action could be // done private LocalDateTime errorDelayIsOver = null; @@ -100,27 +100,18 @@ public class SingleRack extends AbstractOpenemsModbusComponent private LocalDateTime pendingTimestamp; - private final SetAllowedCurrents setAllowedCurrents; + private BatteryProtection batteryProtection = null; public SingleRack() { super(// OpenemsComponent.ChannelId.values(), // StartStoppable.ChannelId.values(), // Battery.ChannelId.values(), // - SingleRack.ChannelId.values() // + SingleRack.ChannelId.values(), // + BatteryProtection.ChannelId.values() // ); - this._setChargeMaxCurrent(SingleRack.CHARGE_MAX_A); this._setChargeMaxVoltage(SingleRack.CHARGE_MAX_V); - this._setDischargeMaxCurrent(SingleRack.DISCHARGE_MAX_A); this._setDischargeMinVoltage(SingleRack.DISCHARGE_MIN_V); - - this.setAllowedCurrents = new SetAllowedCurrents(// - this, // - new SoltaroCellCharacteristic(), // - new SingleRackSettings(), // - this.channel(SingleRack.ChannelId.SYSTEM_ACCEPT_MAX_CHARGE_CURRENT), // - this.channel(SingleRack.ChannelId.SYSTEM_ACCEPT_MAX_DISCHARGE_CURRENT) // - ); } @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) @@ -137,6 +128,12 @@ void activate(ComponentContext context, Config config) throws OpenemsException { } this.modbusBridgeId = config.modbus_id(); this.batteryState = config.batteryState(); + + // Initialize Battery-Protection + this.batteryProtection = BatteryProtection.create(this) // + .applyBatteryProtectionDefinition(new BatteryProtectionDefinitionSoltaro(), this.componentManager) // + .build(); + this._setCapacity(config.capacity() * 1000); this.initializeCallbacks(); } @@ -178,8 +175,7 @@ public void handleEvent(Event event) { switch (event.getTopic()) { case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE: - - this.setAllowedCurrents.act(); + this.batteryProtection.apply(); break; case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: @@ -341,6 +337,7 @@ private boolean isSystemStopped() { /** * Checks whether system has an undefined state. + * * @return true when the system is pending */ private boolean isSystemStatePending() { @@ -355,6 +352,7 @@ private boolean readValueFromBooleanChannel(ChannelId channelId) { /** * Returns the statemachine state. + * * @return the statemachine state */ public State getStateMachineState() { @@ -363,6 +361,7 @@ public State getStateMachineState() { /** * Sets the state. + * * @param state the State */ public void setStateMachineState(State state) { @@ -372,21 +371,19 @@ public void setStateMachineState(State state) { /** * Returns the modbus bridge id. + * * @return the modbus bridge id */ public String getModbusBridgeId() { return this.modbusBridgeId; } - @Override + @Override public String debugLog() { return "SoC:" + this.getSoc() // + "|Discharge:" + this.getDischargeMinVoltage() + ";" + this.getDischargeMaxCurrent() // - + "|Charge:" + this.getChargeMaxVoltage() + ";" + this.getChargeMaxCurrent() - + "|Running: " + this.isSystemRunning() - + "|U: " + this.getVoltage() - + "|I: " + this.getCurrent() - ; + + "|Charge:" + this.getChargeMaxVoltage() + ";" + this.getChargeMaxCurrent() + "|Running: " + + this.isSystemRunning() + "|U: " + this.getVoltage() + "|I: " + this.getCurrent(); } private void startSystem() { @@ -460,10 +457,6 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { .unit(Unit.NONE)), // SYSTEM_INSULATION(Doc.of(OpenemsType.INTEGER) // .unit(Unit.KILOOHM)), // - SYSTEM_ACCEPT_MAX_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // - SYSTEM_ACCEPT_MAX_DISCHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // CLUSTER_1_BATTERY_000_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT)), // CLUSTER_1_BATTERY_001_VOLTAGE(Doc.of(OpenemsType.INTEGER) // @@ -1135,7 +1128,7 @@ public Doc doc() { } } -@Override + @Override protected ModbusProtocol defineModbusProtocol() throws OpenemsException { return new ModbusProtocol(this, // new FC6WriteRegisterTask(0x2010, // @@ -1182,10 +1175,10 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { m(SingleRack.ChannelId.SYSTEM_INSULATION, new UnsignedWordElement(0x2116)) // ), // new FC3ReadRegistersTask(0x2160, Priority.HIGH, // - m(SingleRack.ChannelId.SYSTEM_ACCEPT_MAX_CHARGE_CURRENT, new UnsignedWordElement(0x2160), // - ElementToChannelConverter.SCALE_FACTOR_2), // - m(SingleRack.ChannelId.SYSTEM_ACCEPT_MAX_DISCHARGE_CURRENT, new UnsignedWordElement(0x2161), // - ElementToChannelConverter.SCALE_FACTOR_2) // + m(BatteryProtection.ChannelId.BP_CHARGE_BMS, new UnsignedWordElement(0x2160), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(BatteryProtection.ChannelId.BP_DISCHARGE_BMS, new UnsignedWordElement(0x2161), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1) // ), // new FC3ReadRegistersTask(0x2140, Priority.LOW, // m(new BitsWordElement(0x2140, this) // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionC.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionC.java index cd4fc7fe7f2..fe5a97e57ef 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionC.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionC.java @@ -488,10 +488,6 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId .unit(Unit.MILLIVOLT)), // CLUSTER_1_SYSTEM_INSULATION(Doc.of(OpenemsType.INTEGER) // .unit(Unit.KILOOHM)), // - SYSTEM_MAX_CHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), - SYSTEM_MAX_DISCHARGE_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), POSITIVE_INSULATION(Doc.of(OpenemsType.INTEGER) // .unit(Unit.KILOOHM)), NEGATIVE_INSULATION(Doc.of(OpenemsType.INTEGER) // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionCImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionCImpl.java index acdf43e630e..ce8043dddab 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionCImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/SingleRackVersionCImpl.java @@ -24,9 +24,8 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.battery.api.Battery; -import io.openems.edge.battery.api.SetAllowedCurrents; -import io.openems.edge.battery.soltaro.SoltaroCellCharacteristic; -import io.openems.edge.battery.soltaro.single.SingleRackSettings; +import io.openems.edge.battery.protection.BatteryProtection; +import io.openems.edge.battery.soltaro.BatteryProtectionDefinitionSoltaro; import io.openems.edge.battery.soltaro.single.versionc.statemachine.Context; import io.openems.edge.battery.soltaro.single.versionc.statemachine.StateMachine; import io.openems.edge.battery.soltaro.single.versionc.statemachine.StateMachine.State; @@ -46,6 +45,7 @@ import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC6WriteRegisterTask; import io.openems.edge.common.channel.IntegerWriteChannel; +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.modbusslave.ModbusSlave; @@ -71,28 +71,24 @@ public class SingleRackVersionCImpl extends AbstractOpenemsModbusComponent @Reference protected ConfigurationAdmin cm; + @Reference + protected ComponentManager componentManager; + /** * Manages the {@link State}s of the StateMachine. */ private final StateMachine stateMachine = new StateMachine(State.UNDEFINED); private Config config; - private SetAllowedCurrents setAllowedCurrents; - + private BatteryProtection batteryProtection = null; + public SingleRackVersionCImpl() { super(// OpenemsComponent.ChannelId.values(), // Battery.ChannelId.values(), // StartStoppable.ChannelId.values(), // - SingleRackVersionC.ChannelId.values() // - ); - - this.setAllowedCurrents = new SetAllowedCurrents(// - this, // - new SoltaroCellCharacteristic(), // - new SingleRackSettings(), // - this.channel(SingleRackVersionC.ChannelId.SYSTEM_MAX_CHARGE_CURRENT), // - this.channel(SingleRackVersionC.ChannelId.SYSTEM_MAX_DISCHARGE_CURRENT) // + SingleRackVersionC.ChannelId.values(), // + BatteryProtection.ChannelId.values() // ); } @@ -109,6 +105,11 @@ void activate(ComponentContext context, Config config) throws OpenemsNamedExcept return; } + // Initialize Battery-Protection + this.batteryProtection = BatteryProtection.create(this) // + .applyBatteryProtectionDefinition(new BatteryProtectionDefinitionSoltaro(), this.componentManager) // + .build(); + // Calculate Capacity int capacity = this.config.numberOfSlaves() * this.config.moduleType().getCapacity_Wh(); this._setCapacity(capacity); @@ -138,9 +139,7 @@ public void handleEvent(Event event) { switch (event.getTopic()) { case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE: - - this.setAllowedCurrents.act(); - + this.batteryProtection.apply(); break; case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: @@ -244,7 +243,7 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { m(SingleRackVersionC.ChannelId.VOLTAGE_LOW_PROTECTION, new UnsignedWordElement(0x20F3)), // m(SingleRackVersionC.ChannelId.EMS_COMMUNICATION_TIMEOUT, new UnsignedWordElement(0x20F4)) // ), // - // Single Cluster Running Status Registers + // Single Cluster Running Status Registers new FC3ReadRegistersTask(0x2100, Priority.HIGH, // m(new UnsignedWordElement(0x2100)) // .m(SingleRackVersionC.ChannelId.CLUSTER_1_VOLTAGE, @@ -291,14 +290,10 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { .build(), // m(SingleRackVersionC.ChannelId.CLUSTER_1_AVERAGE_VOLTAGE, new UnsignedWordElement(0x210D)), // m(SingleRackVersionC.ChannelId.CLUSTER_1_SYSTEM_INSULATION, new UnsignedWordElement(0x210E)), // - m(new UnsignedWordElement(0x210F)) // - .m(SingleRackVersionC.ChannelId.SYSTEM_MAX_CHARGE_CURRENT, - ElementToChannelConverter.SCALE_FACTOR_2) // - .build(), // - m(new UnsignedWordElement(0x2110)) // - .m(SingleRackVersionC.ChannelId.SYSTEM_MAX_DISCHARGE_CURRENT, - ElementToChannelConverter.SCALE_FACTOR_2) // - .build(), // + m(BatteryProtection.ChannelId.BP_CHARGE_BMS, new UnsignedWordElement(0x210F), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(BatteryProtection.ChannelId.BP_DISCHARGE_BMS, new UnsignedWordElement(0x2110), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // m(SingleRackVersionC.ChannelId.POSITIVE_INSULATION, new UnsignedWordElement(0x2111)), // m(SingleRackVersionC.ChannelId.NEGATIVE_INSULATION, new UnsignedWordElement(0x2112)), // m(SingleRackVersionC.ChannelId.CLUSTER_RUN_STATE, new UnsignedWordElement(0x2113)), // @@ -332,7 +327,7 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { .bit(14, SingleRackVersionC.ChannelId.LEVEL2_DISCHARGE_TEMP_HIGH) // .bit(15, SingleRackVersionC.ChannelId.LEVEL2_DISCHARGE_TEMP_LOW) // ), // - // Level 1 Alarm: EMS Control to stop charge, discharge, charge&discharge + // Level 1 Alarm: EMS Control to stop charge, discharge, charge&discharge m(new BitsWordElement(0x2141, this) // .bit(0, SingleRackVersionC.ChannelId.LEVEL1_CELL_VOLTAGE_HIGH) // .bit(1, SingleRackVersionC.ChannelId.LEVEL1_TOTAL_VOLTAGE_HIGH) // @@ -351,7 +346,7 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { .bit(14, SingleRackVersionC.ChannelId.LEVEL1_DISCHARGE_TEMP_HIGH) // .bit(15, SingleRackVersionC.ChannelId.LEVEL1_DISCHARGE_TEMP_LOW) // ), // - // Pre-Alarm: Temperature Alarm will active current limication + // Pre-Alarm: Temperature Alarm will active current limication m(new BitsWordElement(0x2142, this) // .bit(0, SingleRackVersionC.ChannelId.PRE_ALARM_CELL_VOLTAGE_HIGH) // .bit(1, SingleRackVersionC.ChannelId.PRE_ALARM_TOTAL_VOLTAGE_HIGH) // @@ -518,8 +513,7 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { new UnsignedWordElement(0x204B), ElementToChannelConverter.SCALE_FACTOR_2), // m(SingleRackVersionC.ChannelId.LEVEL1_CELL_OVER_TEMPERATURE_PROTECTION, new SignedWordElement(0x204C)), // - m(SingleRackVersionC.ChannelId.LEVEL1_CELL_OVER_TEMPERATURE_RECOVER, - new SignedWordElement(0x204D)), // + m(SingleRackVersionC.ChannelId.LEVEL1_CELL_OVER_TEMPERATURE_RECOVER, new SignedWordElement(0x204D)), // m(SingleRackVersionC.ChannelId.LEVEL1_CELL_UNDER_TEMPERATURE_PROTECTION, new SignedWordElement(0x204E)), // m(SingleRackVersionC.ChannelId.LEVEL1_CELL_UNDER_TEMPERATURE_RECOVER, @@ -592,8 +586,7 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { new UnsignedWordElement(0x240B), ElementToChannelConverter.SCALE_FACTOR_2), // m(SingleRackVersionC.ChannelId.LEVEL2_CELL_OVER_TEMPERATURE_PROTECTION, new SignedWordElement(0x240C)), // - m(SingleRackVersionC.ChannelId.LEVEL2_CELL_OVER_TEMPERATURE_RECOVER, - new SignedWordElement(0x240D)), // + m(SingleRackVersionC.ChannelId.LEVEL2_CELL_OVER_TEMPERATURE_RECOVER, new SignedWordElement(0x240D)), // m(SingleRackVersionC.ChannelId.LEVEL2_CELL_UNDER_TEMPERATURE_PROTECTION, new SignedWordElement(0x240E)), // m(SingleRackVersionC.ChannelId.LEVEL2_CELL_UNDER_TEMPERATURE_RECOVER, @@ -647,11 +640,11 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { this.addChannel(channelId); // Add the Modbus Element and map it to the Channel if (type == Type.VOLTAGE) { - elements[j] = m(channelId, new UnsignedWordElement(type.getOffset() + sensorIndex)); + elements[j] = m(channelId, new UnsignedWordElement(type.getOffset() + sensorIndex)); } else { elements[j] = m(channelId, new SignedWordElement(type.getOffset() + sensorIndex)); } - + } // Add a Modbus read task for this module int startAddress = type.getOffset() + i * type.getSensorsPerModule(); diff --git a/io.openems.edge.batteryinverter.api/src/io/openems/edge/batteryinverter/api/HybridManagedSymmetricBatteryInverter.java b/io.openems.edge.batteryinverter.api/src/io/openems/edge/batteryinverter/api/HybridManagedSymmetricBatteryInverter.java new file mode 100644 index 00000000000..7aa41525d63 --- /dev/null +++ b/io.openems.edge.batteryinverter.api/src/io/openems/edge/batteryinverter/api/HybridManagedSymmetricBatteryInverter.java @@ -0,0 +1,85 @@ +package io.openems.edge.batteryinverter.api; + +import org.osgi.annotation.versioning.ProviderType; + +import io.openems.common.channel.Unit; +import io.openems.common.types.OpenemsType; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.startstop.StartStoppable; +import io.openems.edge.ess.api.HybridEss; +import io.openems.edge.ess.dccharger.api.EssDcCharger; + +/** + * Represents a Hybrid Symmetric Battery-Inverter - as part of a + * {@link HybridEss} - that can be controlled. + */ +@ProviderType +public interface HybridManagedSymmetricBatteryInverter + extends ManagedSymmetricBatteryInverter, SymmetricBatteryInverter, StartStoppable { + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + /** + * DC Discharge Power. + * + *

      + *
    • Interface: HybridManagedSymmetricBatteryInverter + *
    • Type: Integer + *
    • Unit: W + *
    • Range: negative values for Charge; positive for Discharge + *
    • This is the + * {@link io.openems.edge.ess.api.SymmetricBatteryInverter.ChannelId#ACTIVE_POWER} + * minus + * {@link io.openems.edge.ess.dccharger.api.EssDcCharger.ChannelId#ACTUAL_POWER}, + * i.e. the power that is actually charged to or discharged from the battery. + *
    + */ + DC_DISCHARGE_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT) // + .text(POWER_DOC_TEXT) // + ), + /** + * DC Charge Energy. + * + *
      + *
    • Interface: HybridEss + *
    • Type: Long + *
    • Unit: Wh + *
    + */ + DC_CHARGE_ENERGY(Doc.of(OpenemsType.LONG) // + .unit(Unit.WATT_HOURS)), + /** + * DC Discharge Energy. + * + *
      + *
    • Interface: HybridEss + *
    • Type: Long + *
    • Unit: Wh + *
    + */ + DC_DISCHARGE_ENERGY(Doc.of(OpenemsType.LONG) // + .unit(Unit.WATT_HOURS)),; + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + public Doc doc() { + return this.doc; + } + } + + /** + * Gets the Surplus Power of the {@link EssDcCharger}s of this + * {@link HybridManagedSymmetricBatteryInverter}. + * + *

    + * This value is usually calculated from the + * {@link EssDcCharger#getActualPower()} when the battery is full + * + * @return the surplus power, or 'null' if there is no surplus power + */ + public Integer getSurplusPower(); +} diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusWorker.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusWorker.java index 1165942a310..57a2378bf15 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusWorker.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusWorker.java @@ -167,6 +167,15 @@ public void onExecuteWrite() { @Override protected void forever() throws InterruptedException { Task task = this.tasksQueue.takeLast(); + + // If there are no tasks in the bridge, there will always be only one + // 'WaitTask'. + if (task instanceof WaitTask && !this.hasTasks()) { + // Make sure to unset the 'SlaveCommunicationFailed' Status in that case. + this.parent._setSlaveCommunicationFailed(false); + return; + } + try { // execute the task int noOfExecutedSubTasks = task.execute(this.parent); @@ -248,6 +257,15 @@ private List getAllWriteTasks() { return this.filterDefectiveComponents(tasks); } + /** + * Does this {@link ModbusWorker} have any Tasks?. + * + * @return true if there are Tasks + */ + private boolean hasTasks() { + return this.writeTasksManager.hasTasks() && this.readTasksManager.hasTasks(); + } + /** * Filters a Multimap with Tasks by Component-ID. For Components that are known * to be defective, only one task is added; otherwise all tasks are added to the diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/Channel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/Channel.java index 9da11c47245..a87c8e6a81e 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/Channel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/Channel.java @@ -46,6 +46,13 @@ * @param the type of the Channel. One out of {@link OpenemsType}. */ public interface Channel { + + /** + * Holds the number of past values for this Channel that are kept in the + * 'pastValues' variable. + */ + public static final int NO_OF_PAST_VALUES = 300; + /** * Gets the ChannelId of this Channel. * diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java index 2c2136d6660..bc5fababa1b 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/Doc.java @@ -3,6 +3,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.channel.ChannelCategory; import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.common.types.OptionsEnum; @@ -106,6 +107,17 @@ public static StateChannelDoc of(Level level) { */ public Unit getUnit(); + /** + * Gets the Persistence Priority. Defaults to VERY_LOW. + * + *

    + * This parameter may be used by persistence services to decide, if the Channel + * should be persisted to the hard disk. + * + * @return the {@link PersistencePriority} + */ + public PersistencePriority getPersistencePriority(); + /** * Sets the descriptive text. Defaults to an empty string. * diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java index a171a31fb1a..5e3664bf31d 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractDoc.java @@ -5,6 +5,7 @@ import java.util.function.Consumer; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; @@ -58,6 +59,30 @@ public AccessMode getAccessMode() { return this.accessMode; } + /** + * PersistencePriority for this Channel. + */ + private PersistencePriority persistencePriority = PersistencePriority.VERY_LOW; + + /** + * Sets the Persistence Priority. Defaults to VERY_LOW. + * + *

    + * This parameter may be used by persistence services to decide, if the Channel + * should be persisted to the hard disk. + * + * @param persistencePriority the {@link PersistencePriority} + */ + public AbstractDoc persistencePriority(PersistencePriority persistencePriority) { + this.persistencePriority = persistencePriority; + return this.self(); + } + + @Override + public PersistencePriority getPersistencePriority() { + return this.persistencePriority; + } + /* * Initial Value */ diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java index 6e7a388a83f..707e02eaf0c 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/internal/AbstractReadChannel.java @@ -23,12 +23,6 @@ public abstract class AbstractReadChannel, T> implements Channel { - /** - * Holds the number of past values for this Channel that are kept in the - * 'pastValues' variable. - */ - public static final int NO_OF_PAST_VALUES = 100; - private final Logger log = LoggerFactory.getLogger(AbstractReadChannel.class); protected final OpenemsComponent parent; diff --git a/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java b/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java index 1b3563fb069..a45aac48fab 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java +++ b/io.openems.edge.common/src/io/openems/edge/common/component/AbstractOpenemsComponent.java @@ -15,6 +15,7 @@ import com.google.common.base.CaseFormat; +import io.openems.common.channel.PersistencePriority; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.EdgeConfig; import io.openems.common.types.OptionsEnum; @@ -238,7 +239,8 @@ private void addChannelsForProperties(ComponentContext context) { .get(io.openems.edge.common.channel.ChannelId.channelIdUpperToCamel(channelName)); if (channel == null) { // Channel does not already exist -> create new Channel - Doc doc = AbstractOpenemsComponent.getDocFromObject(value); + AbstractDoc doc = AbstractOpenemsComponent.getDocFromObject(value); + doc.persistencePriority(PersistencePriority.MEDIUM); io.openems.edge.common.channel.ChannelId channelId = new io.openems.edge.common.channel.ChannelId() { @Override diff --git a/io.openems.edge.common/src/io/openems/edge/common/component/ClockProvider.java b/io.openems.edge.common/src/io/openems/edge/common/component/ClockProvider.java index 25e1855fe01..ba23da44d2f 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/component/ClockProvider.java +++ b/io.openems.edge.common/src/io/openems/edge/common/component/ClockProvider.java @@ -2,8 +2,6 @@ import java.time.Clock; -import io.openems.edge.common.test.TimeLeapClock; - /** * {@link ClockProvider} provides a Clock - real or mocked like * {@link TimeLeapClock}. diff --git a/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java b/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java index c431421dfa1..7836ad0c69a 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java +++ b/io.openems.edge.common/src/io/openems/edge/common/component/OpenemsComponent.java @@ -12,6 +12,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.common.utils.ConfigUtils; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; @@ -182,7 +183,8 @@ default > T channel(io.openems.edge.common.channel.ChannelI public enum ChannelId implements io.openems.edge.common.channel.ChannelId { // Running State of the component. Keep values in sync with 'Level' enum! - STATE(new StateCollectorChannelDoc()); + STATE(new StateCollectorChannelDoc() // + .persistencePriority(PersistencePriority.VERY_HIGH)); private final Doc doc; diff --git a/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java b/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java index 74232f63620..e9075cd5f3b 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java +++ b/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java @@ -2,6 +2,7 @@ import io.openems.common.OpenemsConstants; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; @@ -30,7 +31,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ESS_SOC(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT)), + .unit(Unit.PERCENT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), /** * Ess: Active Power. * @@ -43,6 +45,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ESS_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Reactive Power. @@ -56,6 +59,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ESS_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT) // ), /** @@ -70,6 +74,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ESS_ACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Ess: Active Power L2. @@ -83,6 +88,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ESS_ACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Ess: Active Power L3. @@ -96,6 +102,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ESS_ACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Ess: Discharge Power. @@ -114,6 +121,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ESS_DISCHARGE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Ess: Capacity. @@ -126,7 +134,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ESS_CAPACITY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT_HOURS)), // + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Grid: Active Power. @@ -142,6 +151,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ GRID_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Grid: Active Power L1. @@ -157,6 +167,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ GRID_ACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Grid: Active Power L2. @@ -172,6 +183,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ GRID_ACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Grid: Active Power L3. @@ -187,6 +199,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ GRID_ACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), /** * Grid: Minimum Ever Active Power. @@ -199,7 +212,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ GRID_MIN_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), /** * Grid: Maximum Ever Active Power. * @@ -211,7 +225,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ GRID_MAX_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), /** * Production: Active Power. * @@ -223,7 +238,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: AC Active Power. * @@ -235,7 +251,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_AC_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: AC Active Power L1. * @@ -247,7 +264,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_AC_ACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: AC Active Power L2. * @@ -259,7 +277,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_AC_ACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: AC Active Power L3. * @@ -271,7 +290,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_AC_ACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: DC Actual Power. * @@ -283,7 +303,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_DC_ACTUAL_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: Maximum Ever Active Power. * @@ -295,7 +316,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_MAX_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: Maximum Ever AC Active Power. * @@ -307,7 +329,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_MAX_AC_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: Maximum Ever DC Actual Power. * @@ -319,7 +342,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_MAX_DC_ACTUAL_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Consumption: Active Power. * @@ -333,7 +357,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CONSUMPTION_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Consumption: Active Power L1. * @@ -347,7 +372,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CONSUMPTION_ACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Consumption: Active Power L2. * @@ -361,7 +387,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CONSUMPTION_ACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Consumption: Active Power L3. * @@ -375,7 +402,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CONSUMPTION_ACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Consumption: Maximum Ever Active Power. * @@ -387,7 +415,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CONSUMPTION_MAX_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Grid-Mode. * @@ -397,7 +426,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *

  • Values: '0' = UNDEFINED, '1' = ON GRID, '2' = OFF GRID * */ - GRID_MODE(Doc.of(GridMode.values())), + GRID_MODE(Doc.of(GridMode.values()) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Ess: Max Apparent Power. @@ -409,7 +439,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ESS_MAX_APPARENT_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE)), + .unit(Unit.VOLT_AMPERE) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Ess: Active Charge Energy. * @@ -420,7 +451,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ESS_ACTIVE_CHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Ess: Active Discharge Energy. * @@ -431,7 +463,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ESS_ACTIVE_DISCHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Ess: DC Discharge Energy. * @@ -442,7 +475,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ESS_DC_DISCHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), // + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Ess: DC Charge Energy. * @@ -453,7 +487,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ESS_DC_CHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), // + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Grid: Buy-from-grid Energy ("Production"). * @@ -464,7 +499,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ GRID_BUY_ACTIVE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Grid: Sell-to-grid Energy ("Consumption"). * @@ -475,7 +511,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ GRID_SELL_ACTIVE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: Energy. * @@ -485,7 +522,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_ACTIVE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: AC Energy. * @@ -496,7 +534,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PRODUCTION_AC_ACTIVE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Production: DC Energy. * @@ -508,7 +547,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ // TODO rename to Actual_Energy PRODUCTION_DC_ACTIVE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)), // /** * Consumption: Energy. * @@ -519,7 +559,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CONSUMPTION_ACTIVE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)); + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.VERY_HIGH)); private final Doc doc; @@ -546,8 +587,8 @@ public static ModbusSlaveNatureTable getModbusSlaveNatureTable(AccessMode access .channel(0, ChannelId.ESS_SOC, ModbusType.UINT16) // .channel(1, ChannelId.ESS_ACTIVE_POWER, ModbusType.FLOAT32) // .float32Reserved(3) // ChannelId.ESS_MIN_ACTIVE_POWER - .float32Reserved(5) // ChannelId.ESS_MAX_ACTIVE_POWER - .channel(7, ChannelId.ESS_REACTIVE_POWER, ModbusType.FLOAT32) //ESS_REACTIVE_POWER + .float32Reserved(5) // ChannelId.ESS_MAX_ACTIVE_POWER + .channel(7, ChannelId.ESS_REACTIVE_POWER, ModbusType.FLOAT32) // ESS_REACTIVE_POWER .float32Reserved(9) // ChannelId.ESS_MIN_REACTIVE_POWER .float32Reserved(11) // ChannelId.ESS_MAX_REACTIVE_POWER .channel(13, ChannelId.GRID_ACTIVE_POWER, ModbusType.FLOAT32) // @@ -669,7 +710,7 @@ public default void _setEssActivePower(Integer value) { public default void _setEssActivePower(int value) { this.getEssActivePowerChannel().setNextValue(value); } - + /** * Gets the Channel for {@link ChannelId#ESS_REACTIVE_POWER}. * @@ -680,7 +721,7 @@ public default IntegerReadChannel getEssReactivePowerChannel() { } /** - * Gets the Sum of all Energy Storage System Reactive Power in [var]. + * Gets the Sum of all Energy Storage System Reactive Power in [var]. * {@link ChannelId#ESS_REACTIVE_POWER}. * * @return the Channel {@link Value} @@ -690,8 +731,8 @@ public default Value getEssReactivePower() { } /** - * Internal method to set the 'nextValue' on {@link ChannelId#ESS_REACTIVE_POWER} - * Channel. + * Internal method to set the 'nextValue' on + * {@link ChannelId#ESS_REACTIVE_POWER} Channel. * * @param value the next value */ diff --git a/io.openems.edge.common/src/io/openems/edge/common/taskmanager/MetaTasksManager.java b/io.openems.edge.common/src/io/openems/edge/common/taskmanager/MetaTasksManager.java index 0b6b63ed73e..3b85c4ef5d6 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/taskmanager/MetaTasksManager.java +++ b/io.openems.edge.common/src/io/openems/edge/common/taskmanager/MetaTasksManager.java @@ -109,4 +109,13 @@ public Multimap getAllTasksBySourceId() { return result; } + /** + * Does this {@link TasksManager} have any Tasks?. + * + * @return true if there are Tasks + */ + public boolean hasTasks() { + return !this.tasksManagers.isEmpty(); + } + } \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java b/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java index 5f725ee0d8b..942a9b101ac 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java +++ b/io.openems.edge.common/src/io/openems/edge/common/type/TypeUtils.java @@ -585,7 +585,24 @@ public static Integer averageRounded(Integer... values) { } /** - * Throws a descriptive exception if any of the objects is null. + * Safely finds the min value of all values. + * + * @return the min value; or null if all values are null + */ + public static Integer min(Integer... values) { + Integer result = null; + for (Integer value : values) { + if (result != null && value != null) { + result = Math.min(result, value); + } else if (value != null) { + result = value; + } + } + return result; + } + + /** + * Throws an descriptive exception if the object is null. * * @param description text that is added to the exception * @param objects the objects diff --git a/io.openems.edge.common/test/io/openems/edge/common/type/TypeUtilsTest.java b/io.openems.edge.common/test/io/openems/edge/common/type/TypeUtilsTest.java index e7b35ac3957..ed60fdc7ba0 100644 --- a/io.openems.edge.common/test/io/openems/edge/common/type/TypeUtilsTest.java +++ b/io.openems.edge.common/test/io/openems/edge/common/type/TypeUtilsTest.java @@ -1,6 +1,6 @@ package io.openems.edge.common.type; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -39,4 +39,12 @@ public void testAverageRounded() { assertEquals(Integer.valueOf(3), TypeUtils.averageRounded(2, null, 3)); } + @Test + public void testMin() { + assertEquals(25, (int) TypeUtils.min(null, 25, null, 40, null)); + assertEquals(null, TypeUtils.min((Double) null, null, null)); + assertEquals(17, (int) TypeUtils.min(null, 17, 25, 40)); + assertEquals(34, (int) TypeUtils.min(null, 34, 40)); + } + } diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java index a7b836e1067..2cbebcd29e1 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApi.java @@ -3,8 +3,11 @@ import org.ops4j.pax.logging.spi.PaxAppender; import org.osgi.service.event.EventHandler; +import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.StringReadChannel; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.controller.api.Controller; @@ -13,7 +16,16 @@ public interface BackendApi extends Controller, OpenemsComponent, PaxAppender, E public enum ChannelId implements io.openems.edge.common.channel.ChannelId { API_WORKER_LOG(Doc.of(OpenemsType.STRING) // - .text("Logs Write-Commands via ApiWorker")); // + .text("Logs Write-Commands via ApiWorker")), // + UNABLE_TO_SEND(Doc.of(Level.WARNING) + // Make sure this is always persisted, as it is required for resending + .persistencePriority(PersistencePriority.VERY_HIGH)), // + LAST_SUCCESSFUL_RESEND(Doc.of(OpenemsType.LONG) // + // Make sure this is always persisted, as it is required for resending + .persistencePriority(PersistencePriority.VERY_HIGH) // + .text("Latest timestamp of successfully resent data")) // + // TODO: resend algorithm still needs to be implemented + ; private final Doc doc; @@ -35,4 +47,13 @@ public Doc doc() { public default StringReadChannel getApiWorkerLogChannel() { return this.channel(ChannelId.API_WORKER_LOG); } + + /** + * Gets the Channel for {@link ChannelId#UNABLE_TO_SEND}. + * + * @return the Channel + */ + public default StateChannel getUnableToSendChannel() { + return this.channel(ChannelId.UNABLE_TO_SEND); + } } diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApiImpl.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApiImpl.java index 1374d6ed00d..875ed6a5e36 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApiImpl.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendApiImpl.java @@ -33,6 +33,7 @@ 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.cycle.Cycle; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.common.ApiWorker; @@ -51,18 +52,16 @@ public class BackendApiImpl extends AbstractOpenemsComponent implements BackendApi, Controller, OpenemsComponent, PaxAppender, EventHandler { - protected static final int DEFAULT_NO_OF_CYCLES = 10; protected static final String COMPONENT_NAME = "Controller.Api.Backend"; - protected final BackendWorker worker = new BackendWorker(this); + protected final SendChannelValuesWorker sendChannelValuesWorker = new SendChannelValuesWorker(this); protected final ApiWorker apiWorker = new ApiWorker(this); private final Logger log = LoggerFactory.getLogger(BackendApiImpl.class); protected WebsocketClient websocket = null; - protected int noOfCycles = DEFAULT_NO_OF_CYCLES; // default, is going to be overwritten by config - protected boolean debug = false; + protected Config config; // Used for SubscribeSystemLogRequests private boolean isSystemLogSubscribed = false; @@ -73,6 +72,9 @@ public class BackendApiImpl extends AbstractOpenemsComponent @Reference protected ComponentManager componentManager; + @Reference + protected Cycle cycle; + public BackendApiImpl() { super(// OpenemsComponent.ChannelId.values(), // @@ -84,9 +86,8 @@ public BackendApiImpl() { @Activate void activate(ComponentContext context, Config config) { + this.config = config; super.activate(context, config.id(), config.alias(), config.enabled()); - this.noOfCycles = config.noOfCycles(); - this.debug = config.debug(); if (!this.isEnabled()) { return; @@ -119,15 +120,12 @@ void activate(ComponentContext context, Config config) { // Create Websocket instance this.websocket = new WebsocketClient(this, COMPONENT_NAME + ":" + this.id(), uri, httpHeaders, proxy); this.websocket.start(); - - // Activate worker - this.worker.activate(config.id()); } @Deactivate protected void deactivate() { super.deactivate(); - this.worker.deactivate(); + this.sendChannelValuesWorker.deactivate(); if (this.websocket != null) { this.websocket.stop(); } @@ -181,10 +179,11 @@ public void handleEvent(Event event) { } switch (event.getTopic()) { case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: - this.worker.triggerNextRun(); + this.sendChannelValuesWorker.collectData(); break; case EdgeEventConstants.TOPIC_CONFIG_UPDATE: + // Send new EdgeConfig EdgeConfig config = (EdgeConfig) event.getProperty(EdgeEventConstants.TOPIC_CONFIG_UPDATE_KEY); EdgeConfigNotification message = new EdgeConfigNotification(config); WebsocketClient ws = this.websocket; @@ -192,6 +191,14 @@ public void handleEvent(Event event) { return; } ws.sendMessage(message); + + // Trigger sending of all channel values, because a Component might have + // disappeared + this.sendChannelValuesWorker.sendValuesOfAllChannelsOnce(); } } + + public boolean isConnected() { + return this.websocket.isConnected(); + } } diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java index c0b2c2c3247..be5ac5e371f 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendWorker.java @@ -1,261 +1,177 @@ package io.openems.edge.controller.api.backend; +import java.time.Duration; import java.time.Instant; import java.util.HashMap; -import java.util.Iterator; +import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; -import com.google.common.collect.EvictingQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableTable; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import io.openems.common.channel.AccessMode; -import io.openems.common.jsonrpc.base.JsonrpcMessage; import io.openems.common.jsonrpc.notification.TimestampedDataNotification; import io.openems.common.types.ChannelAddress; -import io.openems.common.types.OpenemsType; -import io.openems.common.worker.AbstractCycleWorker; -import io.openems.edge.common.channel.EnumReadChannel; -import io.openems.edge.common.type.slidingvalue.DoubleSlidingValue; -import io.openems.edge.common.type.slidingvalue.FloatSlidingValue; -import io.openems.edge.common.type.slidingvalue.IntegerSlidingValue; -import io.openems.edge.common.type.slidingvalue.LatestSlidingValue; -import io.openems.edge.common.type.slidingvalue.LongSlidingValue; -import io.openems.edge.common.type.slidingvalue.ShortSlidingValue; -import io.openems.edge.common.type.slidingvalue.SlidingValue; - -class BackendWorker extends AbstractCycleWorker { +import io.openems.edge.common.component.OpenemsComponent; - private static final int MAX_CACHED_MESSAGES = 1000; +public class BackendWorker { - // private final Logger log = LoggerFactory.getLogger(BackendWorker.class); + private static final int SEND_VALUES_OF_ALL_CHANNELS_AFTER_SECONDS = 300; /* 5 minutes */ + private final Logger log = LoggerFactory.getLogger(BackendWorker.class); private final BackendApiImpl parent; + private final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, + new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardOldestPolicy()); - // Counts the number of Cycles till data is sent to Backend. - private int cycleCount = 0; - - // Holds an current NoOfCycles - private Optional increasedNoOfCycles = Optional.empty(); - - // Current values - private final ConcurrentHashMap> data = new ConcurrentHashMap<>(); + // Component-ID to Channel-ID to value + private ImmutableTable lastValues = ImmutableTable.of(); - // Unsent queue (FIFO) - private EvictingQueue unsent = EvictingQueue.create(MAX_CACHED_MESSAGES); - - // By default the worker reads and sends only changed values. If this variable - // is set to 'false', it sends all values once. - private final AtomicBoolean sendChangedValuesOnly = new AtomicBoolean(false); + private Instant lastSendValuesOfAllChannels = Instant.MIN; protected BackendWorker(BackendApiImpl parent) { this.parent = parent; } - @Override - public void activate(String name) { - super.activate(name); - } - - @Override - public void deactivate() { - super.deactivate(); - } - /** - * Triggers sending all Channel values once. After executing once, this is reset - * automatically to default 'send changed values only' mode. + * Called synchronously on AFTER_PROCESS_IMAGE event. Collects all the data and + * triggers asynchronous sending. */ - public void sendValuesOfAllChannelsOnce() { - this.sendChangedValuesOnly.set(false); - this.triggerNextRun(); - } + public synchronized void collectData() { + Instant now = Instant.now(this.parent.componentManager.getClock()); - @Override - protected void forever() { - // Update the data from ChannelValues - this.updateData(); - - // Increase CycleCount - if (++this.cycleCount < this.parent.noOfCycles) { - // Stop here if not reached CycleCount - return; + // Send values of all Channels once in a while + if (Duration.between(this.lastSendValuesOfAllChannels, now) + .getSeconds() > SEND_VALUES_OF_ALL_CHANNELS_AFTER_SECONDS) { + this.lastValues = ImmutableTable.of(); } + if (this.lastValues.isEmpty()) { + this.lastSendValuesOfAllChannels = now; + } + + final List enabledComponents = this.parent.componentManager.getEnabledComponents(); - /* - * Reached CycleCount -> Send data - */ - // Reset CycleCount - this.cycleCount = 0; + // Update the data from ChannelValues + final ImmutableTable allValues = this.collectData(enabledComponents); - // resets the mode to 'send changed values only' - boolean sendChangedValuesOnly = this.sendChangedValuesOnly.getAndSet(true); + // Get timestamp and round to Global Cycle-Time + final int cycleTime = this.parent.cycle.getCycleTime(); + final long timestamp = now.toEpochMilli() / cycleTime * cycleTime; // Prepare message values Map sendValues = new HashMap<>(); - if (sendChangedValuesOnly) { - // Only Changed Values - for (Entry> entry : this.data.entrySet()) { - JsonElement changedValueOrNull = entry.getValue().getChangedValueOrNull(); - if (changedValueOrNull != null) { - sendValues.put(entry.getKey(), changedValueOrNull); - } - } - } else { - // All Values - for (Entry> entry : this.data.entrySet()) { - sendValues.put(entry.getKey(), entry.getValue().getValue()); - } - } - - boolean canSendFromCache; + // Collect Changed values + allValues.rowMap().entrySet().parallelStream() // + .forEach(row -> { + row.getValue().entrySet().parallelStream() // + .forEach(column -> { + if (!Objects.equals(column.getValue(), + this.lastValues.get(row.getKey(), column.getKey()))) { + sendValues.put(new ChannelAddress(row.getKey(), column.getKey()), + column.getValue()); + } + }); + }); - /* - * send, if list is not empty - */ - if (!sendValues.isEmpty()) { - // Get timestamp and round to Cycle-Time - int cycleTime = this.getCycleTime(); - long timestamp = Instant.now(this.parent.componentManager.getClock()).toEpochMilli() / cycleTime - * cycleTime; + // Update disappeared components + final Set enabledComponentIds = enabledComponents.stream() // + .map(c -> c.id()) // + .collect(Collectors.toSet()); + this.lastValues.rowMap().entrySet().stream() // + .filter(row -> !enabledComponentIds.contains(row.getKey())) // + .forEach(row -> { + row.getValue().entrySet().parallelStream() // + .forEach(column -> { + sendValues.put(new ChannelAddress(row.getKey(), column.getKey()), JsonNull.INSTANCE); + }); + }); - // create JSON-RPC notification - TimestampedDataNotification message = new TimestampedDataNotification(); - message.add(timestamp, sendValues); + // Keep values for next run + this.lastValues = allValues; - // reset cycleTime to default - this.resetNoOfCycles(); + // Nothing to send + if (sendValues.isEmpty()) { + return; + } - boolean wasSent = this.parent.websocket.sendMessage(message); - if (!wasSent) { - // increase cycleTime - this.increaseNoOfCycles(); + // create JSON-RPC notification + TimestampedDataNotification message = new TimestampedDataNotification(); + message.add(timestamp, sendValues); - // cache data for later - this.unsent.add(message); - } + // Add to Task Queue + this.executor.execute(new SendTask(this, message)); + } - canSendFromCache = wasSent; - } else { - canSendFromCache = true; - } + /** + * Triggers sending all Channel values once. After executing once, this is reset + * automatically to default 'send changed values only' mode. + */ + public synchronized void sendValuesOfAllChannelsOnce() { + this.lastValues = ImmutableTable.of(); + } - // send from cache - if (canSendFromCache && !this.unsent.isEmpty()) { - for (Iterator iterator = this.unsent.iterator(); iterator.hasNext();) { - JsonrpcMessage cached = iterator.next(); - boolean cacheWasSent = this.parent.websocket.sendMessage(cached); - if (cacheWasSent) { - // sent successfully -> remove from cache & try next - iterator.remove(); + public void deactivate() { + // Shutdown executor + if (this.executor != null) { + try { + this.executor.shutdown(); + this.executor.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + this.log.warn("tasks interrupted"); + } finally { + if (!this.executor.isTerminated()) { + this.log.warn("cancel non-finished tasks"); } + this.executor.shutdownNow(); } } } /** - * Cycles through all Channels and updates the value. + * Cycles through all Channels and collects the value. + * + * @param enabledComponents the enabled components + * @return collected data */ - private void updateData() { - this.parent.componentManager.getEnabledComponents().parallelStream() // - .filter(c -> c.isEnabled()) // + private ImmutableTable collectData(List enabledComponents) { + return enabledComponents.parallelStream() // .flatMap(component -> component.channels().parallelStream()) // .filter(channel -> // Ignore WRITE_ONLY Channels channel.channelDoc().getAccessMode() == AccessMode.READ_ONLY || channel.channelDoc().getAccessMode() == AccessMode.READ_WRITE) - .forEach(channel -> { - ChannelAddress address = channel.address(); - Object value = channel.value().get(); - - // Get existing SlidingValue object or add new one - SlidingValue slidingValue = this.data.get(address); + .collect(ImmutableTable.toImmutableTable(c -> c.address().getComponentId(), + c -> c.address().getChannelId(), c -> c.value().asJson())); + } - if (slidingValue == null) { - // Create new SlidingValue object - if (channel instanceof EnumReadChannel) { - slidingValue = new LatestSlidingValue(OpenemsType.INTEGER); - } else { - switch (channel.getType()) { - case INTEGER: - slidingValue = new IntegerSlidingValue(); - break; - case DOUBLE: - slidingValue = new DoubleSlidingValue(); - break; - case FLOAT: - slidingValue = new FloatSlidingValue(); - break; - case LONG: - slidingValue = new LongSlidingValue(); - break; - case SHORT: - slidingValue = new ShortSlidingValue(); - break; - case BOOLEAN: - case STRING: - slidingValue = new LatestSlidingValue(channel.getType()); - break; - } - } - this.data.put(address, slidingValue); - } + private static class SendTask implements Runnable { - // Add Value to SlidingValue object - if (slidingValue instanceof LatestSlidingValue) { - ((LatestSlidingValue) slidingValue).addValue(value); - } else { - switch (channel.getType()) { - case INTEGER: - ((IntegerSlidingValue) slidingValue).addValue((Integer) value); - break; - case DOUBLE: - ((DoubleSlidingValue) slidingValue).addValue((Double) value); - break; - case FLOAT: - ((FloatSlidingValue) slidingValue).addValue((Float) value); - break; - case LONG: - ((LongSlidingValue) slidingValue).addValue((Long) value); - break; - case SHORT: - ((ShortSlidingValue) slidingValue).addValue((Short) value); - break; - case BOOLEAN: - case STRING: - // already covered as they are of type LatestSlidingValue - break; - } - } - }); - } + private final BackendWorker parent; + private final TimestampedDataNotification message; - /** - * NoOfCycles is adjusted if connection to Backend fails. This method increases - * the NoOfCycles. - */ - private void increaseNoOfCycles() { - int increasedNoOfCycles; - if (this.increasedNoOfCycles.isPresent()) { - increasedNoOfCycles = this.increasedNoOfCycles.get(); - } else { - increasedNoOfCycles = this.parent.noOfCycles; - } - if (increasedNoOfCycles < 60) { - increasedNoOfCycles++; + public SendTask(BackendWorker parent, TimestampedDataNotification message) { + this.parent = parent; + this.message = message; } - this.increasedNoOfCycles = Optional.of(increasedNoOfCycles); - } - /** - * NoOfCycles is adjusted if connection to Backend fails. This method resets it - * to configured or default value. - */ - private void resetNoOfCycles() { - this.increasedNoOfCycles = Optional.empty(); - } + @Override + public void run() { + // Try to send; drop message if not possible to send (i.e. task is not + // rescheduled) + boolean wasSent = this.parent.parent.websocket.sendMessage(this.message); + + // Set the UNABLE_TO_SEND channel + this.parent.parent.getUnableToSendChannel().setNextValue(!wasSent); + } -} \ No newline at end of file + }; +} diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java index b0bb8ea5686..df7a64e63a9 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/Config.java @@ -6,6 +6,8 @@ import org.osgi.service.metatype.annotations.AttributeType; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.common.channel.PersistencePriority; + @ObjectClassDefinition(// name = "Controller Api Backend", // description = "This controller connects to OpenEMS Backend") @@ -26,9 +28,6 @@ @AttributeDefinition(name = "Uri", description = "The connection Uri to OpenEMS Backend.") String uri() default "ws://localhost:8081"; - @AttributeDefinition(name = "No. of Cycles", description = "How many Cycles till data is sent to OpenEMS Backend.") - int noOfCycles() default BackendApiImpl.DEFAULT_NO_OF_CYCLES; - @AttributeDefinition(name = "Proxy Address", description = "The IP address or hostname of the proxy server.") String proxyAddress() default ""; @@ -41,8 +40,11 @@ @AttributeDefinition(name = "Api-Timeout", description = "Sets the timeout in seconds for updates on Channels set by this Api.") int apiTimeout() default 60; - @AttributeDefinition(name = "Enable Debug mode") - boolean debug() default false; + @AttributeDefinition(name = "Persistence Priority", description = "Send only Channels with a Persistence Priority greater-or-equals this.") + PersistencePriority persistencePriority() default PersistencePriority.VERY_LOW; + + @AttributeDefinition(name = "Debug Mode", description = "Activates the debug mode") + boolean debugMode() default false; String webconsole_configurationFactory_nameHint() default "Controller Api Backend [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java index 6ff3fb507d0..424262dd01b 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnOpen.java @@ -28,7 +28,7 @@ public void run(WebSocket ws, JsonObject handshake) { this.parent.websocket.sendMessage(message); // Send all Channel values - this.parent.worker.sendValuesOfAllChannelsOnce(); + this.parent.sendChannelValuesWorker.sendValuesOfAllChannelsOnce(); } } diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/SendChannelValuesWorker.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/SendChannelValuesWorker.java new file mode 100644 index 00000000000..217148b8f11 --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/SendChannelValuesWorker.java @@ -0,0 +1,210 @@ +package io.openems.edge.controller.api.backend; + +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; +import com.google.gson.JsonElement; + +import io.openems.common.channel.AccessMode; +import io.openems.common.jsonrpc.notification.TimestampedDataNotification; +import io.openems.common.types.ChannelAddress; +import io.openems.edge.common.component.OpenemsComponent; + +/** + * Method {@link #collectData()} is called Synchronously with the Core.Cycle to + * collect values of Channels. Sending of values is then delegated to an + * asynchronous task. + * + *

    + * The logic tries to send changed values once per Cycle and all values once + * every {@link #SEND_VALUES_OF_ALL_CHANNELS_AFTER_SECONDS}. + */ +public class SendChannelValuesWorker { + + private static final int SEND_VALUES_OF_ALL_CHANNELS_AFTER_SECONDS = 300; /* 5 minutes */ + + private final Logger log = LoggerFactory.getLogger(SendChannelValuesWorker.class); + + private final BackendApiImpl parent; + private final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, + new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardOldestPolicy()); + + /** + * If true: next 'send' sends all channel values. + */ + private AtomicBoolean sendValuesOfAllChannels = new AtomicBoolean(true); + + /** + * Keeps the last timestamp when all channel values were sent. + */ + private Instant lastSendValuesOfAllChannels = Instant.MIN; + + /** + * Keeps the values of last successful send. + */ + private Table lastAllValues = ImmutableTable.of(); + + protected SendChannelValuesWorker(BackendApiImpl parent) { + this.parent = parent; + } + + /** + * Called synchronously on AFTER_PROCESS_IMAGE event. Collects all the data and + * triggers asynchronous sending. + */ + public synchronized void collectData() { + Instant now = Instant.now(this.parent.componentManager.getClock()); + + // Update the values of all channels + final List enabledComponents = this.parent.componentManager.getEnabledComponents(); + final ImmutableTable allValues = this.collectData(enabledComponents); + + // Add to send Queue + this.executor.execute(new SendTask(this, now, allValues)); + } + + /** + * Triggers sending all Channel values once. + */ + public synchronized void sendValuesOfAllChannelsOnce() { + this.sendValuesOfAllChannels.set(true); + } + + public void deactivate() { + // Shutdown executor + if (this.executor != null) { + try { + this.executor.shutdown(); + this.executor.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + this.log.warn("tasks interrupted"); + } finally { + if (!this.executor.isTerminated()) { + this.log.warn("cancel non-finished tasks"); + } + this.executor.shutdownNow(); + } + } + } + + /** + * Cycles through all Channels and collects the value. + * + * @param enabledComponents the enabled components + * @return collected data + */ + private ImmutableTable collectData(List enabledComponents) { + try { + return enabledComponents.parallelStream() // + .flatMap(component -> component.channels().parallelStream()) // + .filter(channel -> // Ignore WRITE_ONLY Channels + channel.channelDoc().getAccessMode() != AccessMode.WRITE_ONLY // + // Ignore Low-Priority Channels + && channel.channelDoc().getPersistencePriority() + .isAtLeast(this.parent.config.persistencePriority())) + .collect(ImmutableTable.toImmutableTable(c -> c.address().getComponentId(), + c -> c.address().getChannelId(), c -> c.value().asJson())); + } catch (Exception e) { + // ConcurrentModificationException can happen if Channels are dynamically added + // or removed + return ImmutableTable.of(); + } + } + + /* + * From here things run asynchronously. + */ + + private static class SendTask implements Runnable { + + private final SendChannelValuesWorker parent; + private final Instant timestamp; + private final ImmutableTable allValues; + + public SendTask(SendChannelValuesWorker parent, Instant timestamp, + ImmutableTable allValues) { + this.parent = parent; + this.timestamp = timestamp; + this.allValues = allValues; + } + + @Override + public void run() { + // Holds the data of the last successful send. If the table is empty, it is also + // used as a marker to send all data. + final Table lastAllValues; + + if (this.parent.sendValuesOfAllChannels.getAndSet(false)) { + // Send values of all Channels once in a while + lastAllValues = ImmutableTable.of(); + + } else if (Duration.between(this.parent.lastSendValuesOfAllChannels, this.timestamp) + .getSeconds() > SEND_VALUES_OF_ALL_CHANNELS_AFTER_SECONDS) { + // Send values of all Channels if explicitly asked for + lastAllValues = ImmutableTable.of(); + + } else { + // Actually use the kept 'lastSentValues' + lastAllValues = this.parent.lastAllValues; + } + + // Round timestamp to Global Cycle-Time + final int cycleTime = this.parent.parent.cycle.getCycleTime(); + final long timestampMillis = this.timestamp.toEpochMilli() / cycleTime * cycleTime; + + // Prepare message values + Map sendValuesMap = new HashMap<>(); + + // Collect Changed values + for (Entry> row : allValues.rowMap().entrySet()) { + for (Entry column : row.getValue().entrySet()) { + if (!Objects.equals(column.getValue(), lastAllValues.get(row.getKey(), column.getKey()))) { + sendValuesMap.put(new ChannelAddress(row.getKey(), column.getKey()), column.getValue()); + } + } + } + + // Create JSON-RPC notification + TimestampedDataNotification message = new TimestampedDataNotification(); + message.add(timestampMillis, sendValuesMap); + + // Debug-Log + if (this.parent.parent.config.debugMode()) { + this.parent.parent.logInfo(this.parent.log, + "Sending [" + sendValuesMap.size() + " values]: " + sendValuesMap); + } + + // Try to send + boolean wasSent = this.parent.parent.websocket.sendMessage(message); + + // Set the UNABLE_TO_SEND channel + this.parent.parent.getUnableToSendChannel().setNextValue(!wasSent); + + if (wasSent) { + // Successfully sent: update information for next runs + this.parent.lastAllValues = allValues; + if (lastAllValues.isEmpty()) { + // 'lastSentValues' was empty, i.e. all values were sent + this.parent.lastSendValuesOfAllChannels = this.timestamp; + } + } + + } + + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java index c3668fb5c8d..400e659c52a 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java @@ -74,4 +74,8 @@ protected void logInfo(Logger log, String message) { protected void logWarn(Logger log, String message) { this.parent.logWarn(log, message); } + + public boolean isConnected() { + return this.ws.isOpen(); + } } diff --git a/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/BackendApiImplTest.java b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/BackendApiImplTest.java new file mode 100644 index 00000000000..4b0bae3ea9e --- /dev/null +++ b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/BackendApiImplTest.java @@ -0,0 +1,153 @@ +package io.openems.edge.controller.api.backend; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.Proxy.Type; +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import java.util.function.BiConsumer; + +import org.java_websocket.WebSocket; +import org.junit.Test; + +import com.google.gson.JsonObject; + +import io.openems.common.OpenemsConstants; +import io.openems.common.channel.PersistencePriority; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.base.JsonrpcNotification; +import io.openems.common.jsonrpc.notification.TimestampedDataNotification; +import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.JsonUtils; +import io.openems.common.websocket.DummyWebsocketServer; +import io.openems.edge.common.sum.DummySum; +import io.openems.edge.common.sum.Sum; +import io.openems.edge.common.test.AbstractComponentTest.TestCase; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyComponentManager; +import io.openems.edge.common.test.DummyCycle; +import io.openems.edge.common.test.TimeLeapClock; + +public class BackendApiImplTest { + + private static final String CTRL_ID = "ctrl0"; + + private static final String SUM_ID = OpenemsConstants.SUM_ID; + private static final ChannelAddress SUM_GRID_ACTIVE_POWER = new ChannelAddress(SUM_ID, + Sum.ChannelId.GRID_ACTIVE_POWER.id()); + private static final ChannelAddress SUM_PRODUCTION_ACTIVE_POWER = new ChannelAddress(SUM_ID, + Sum.ChannelId.PRODUCTION_ACTIVE_POWER.id()); + + private static class TimestampedDataNotificationHandler implements io.openems.common.websocket.OnNotification { + + protected boolean wasCalled = false; + + private BiConsumer callback = (timestamp, values) -> { + }; + + public TimestampedDataNotificationHandler() { + } + + public void onNotification(BiConsumer callback) { + this.wasCalled = false; + this.callback = callback; + } + + public void waitForCallback(int timeout) throws InterruptedException, OpenemsException { + Instant start = Instant.now(); + while (!this.wasCalled) { + if (Duration.between(start, Instant.now()).getSeconds() > timeout) { + throw new OpenemsException("Timeout [" + timeout + "s]"); + } + Thread.sleep(100); + } + } + + @Override + public void run(WebSocket websocket, JsonrpcNotification notification) throws OpenemsNamedException { + if (notification.getMethod().equals(TimestampedDataNotification.METHOD)) { + for (String timestamp : notification.getParams().keySet()) { + JsonObject values = JsonUtils.getAsJsonObject(notification.getParams().get(timestamp)); + System.out.println(values); + this.callback.accept(Long.valueOf(timestamp), values); + this.wasCalled = true; + return; + } + } + } + + } + + @Test + public void test() throws Exception { + TimestampedDataNotificationHandler handler = new TimestampedDataNotificationHandler(); + + try (final DummyWebsocketServer server = DummyWebsocketServer.create() // + .onNotification(handler) // + .build()) { + int port = server.startBlocking(); + + final TimeLeapClock clock = new TimeLeapClock( + Instant.ofEpochSecond(1577836800L) /* starts at 1. January 2020 00:00:00 */, ZoneOffset.UTC); + final BackendApiImpl sut = new BackendApiImpl(); + ComponentTest test = new ComponentTest(sut) // + .addReference("componentManager", new DummyComponentManager(clock)) // + .addReference("cycle", new DummyCycle(1000)) // + .addComponent(new DummySum()) // + .activate(MyConfig.create() // + .setId(CTRL_ID) // + .setUri("ws://localhost:" + port) // + .setApikey("12345") // + .setProxyType(Type.DIRECT) // + .setProxyAddress("") // + .setPersistencePriority(PersistencePriority.VERY_LOW) // + .build()); + + while (!sut.isConnected()) { + Thread.sleep(100); + } + + // All Values initially + handler.onNotification((timestamp, values) -> { + assertTrue(values.size() > 50); // all values + }); + test.next(new TestCase()); // + handler.waitForCallback(5000); + + // Only changed value + handler.onNotification((timestamp, values) -> { + assertTrue(values.size() == 1); // exactly one value + assertEquals(Integer.valueOf(1000), + JsonUtils.getAsOptionalInt(values, SUM_GRID_ACTIVE_POWER.toString()).get()); + }); + test.next(new TestCase() // + .input(SUM_GRID_ACTIVE_POWER, 1000) // + ); + handler.waitForCallback(5000); + + // Only changed value + handler.onNotification((timestamp, values) -> { + assertTrue(values.size() == 1); // exactly one value + assertEquals(Integer.valueOf(2000), + JsonUtils.getAsOptionalInt(values, SUM_PRODUCTION_ACTIVE_POWER.toString()).get()); + }); + test.next(new TestCase() // + .input(SUM_PRODUCTION_ACTIVE_POWER, 2000) // + ); + handler.waitForCallback(5000); + + // All values after 5 minutes + handler.onNotification((timestamp, values) -> { + assertTrue(values.size() > 50); // all values + }); + test.next(new TestCase() // + .timeleap(clock, 6, ChronoUnit.MINUTES)); + handler.waitForCallback(5000); + } + } + +} diff --git a/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/MyConfig.java b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/MyConfig.java new file mode 100644 index 00000000000..46771968219 --- /dev/null +++ b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/MyConfig.java @@ -0,0 +1,131 @@ +package io.openems.edge.controller.api.backend; + +import java.net.Proxy.Type; + +import io.openems.common.channel.PersistencePriority; +import io.openems.edge.common.test.AbstractComponentConfig; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + protected static class Builder { + private String id; + public String apikey; + public String uri; + public String proxyAddress; + public int proxyPort; + public Type proxyType; + public int apiTimeout; + public PersistencePriority persistencePriority; + public boolean debugMode; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setApikey(String apikey) { + this.apikey = apikey; + return this; + } + + public Builder setUri(String uri) { + this.uri = uri; + return this; + } + + public Builder setProxyAddress(String proxyAddress) { + this.proxyAddress = proxyAddress; + return this; + } + + public Builder setProxyPort(int proxyPort) { + this.proxyPort = proxyPort; + return this; + } + + public Builder setProxyType(Type proxyType) { + this.proxyType = proxyType; + return this; + } + + public Builder setApiTimeout(int apiTimeout) { + this.apiTimeout = apiTimeout; + return this; + } + + public Builder setPersistencePriority(PersistencePriority persistencePriority) { + this.persistencePriority = persistencePriority; + return this; + } + + public Builder setDebugMode(boolean debugMode) { + this.debugMode = debugMode; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public String apikey() { + return this.builder.apikey; + } + + @Override + public String uri() { + return this.builder.uri; + } + + @Override + public String proxyAddress() { + return this.builder.proxyAddress; + } + + @Override + public int proxyPort() { + return this.builder.proxyPort; + } + + @Override + public Type proxyType() { + return this.builder.proxyType; + } + + @Override + public int apiTimeout() { + return this.builder.apiTimeout; + } + + @Override + public PersistencePriority persistencePriority() { + return this.builder.persistencePriority; + } + + @Override + public boolean debugMode() { + return this.builder.debugMode; + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxRequest.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxRequest.java index 0d702b0be8f..a99c5a1b1f9 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxRequest.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolExportXlsxRequest.java @@ -1,7 +1,5 @@ package io.openems.edge.controller.api.modbus.jsonrpc; -import java.util.UUID; - import com.google.gson.JsonObject; import io.openems.common.jsonrpc.base.JsonrpcRequest; @@ -23,11 +21,11 @@ public class GetModbusProtocolExportXlsxRequest extends JsonrpcRequest { public static final String METHOD = "getModbusProtocolExportXlsx"; public GetModbusProtocolExportXlsxRequest() { - this(UUID.randomUUID()); + super(METHOD); } - public GetModbusProtocolExportXlsxRequest(UUID id) { - super(id, METHOD); + private GetModbusProtocolExportXlsxRequest(JsonrpcRequest request) { + super(request, METHOD); } @Override diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolRequest.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolRequest.java index ed07c9415c2..4a4bbfa9adc 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolRequest.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/jsonrpc/GetModbusProtocolRequest.java @@ -1,7 +1,5 @@ package io.openems.edge.controller.api.modbus.jsonrpc; -import java.util.UUID; - import com.google.gson.JsonObject; import io.openems.common.jsonrpc.base.JsonrpcRequest; @@ -24,11 +22,11 @@ public class GetModbusProtocolRequest extends JsonrpcRequest { public static final String METHOD = "getModbusProtocol"; public GetModbusProtocolRequest() { - this(UUID.randomUUID()); + super(METHOD); } - public GetModbusProtocolRequest(UUID id) { - super(id, METHOD); + private GetModbusProtocolRequest(JsonrpcRequest request) { + super(request, METHOD); } @Override diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java index f00cb4ba612..fcc9b771457 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnOpen.java @@ -47,7 +47,7 @@ public void run(WebSocket ws, JsonObject handshake) { // send authentication notification AuthenticateWithSessionIdNotification notification = new AuthenticateWithSessionIdNotification( - token, Utils.getEdgeMetadata(user.getRole())); + token.toString(), Utils.getEdgeMetadata(user.getRole())); this.parent.server.sendMessage(ws, notification); // log diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java index 7c534e5e3b4..026c3960df3 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java @@ -183,7 +183,7 @@ private CompletableFuture handleAuthenticateWithPassword this.parent.sessionTokens.put(wsData.getSessionToken(), user); // TODO unset on logout! return CompletableFuture.completedFuture(new AuthenticateWithPasswordResponse(request.getId(), - wsData.getSessionToken(), Utils.getEdgeMetadata(user.getRole()))); + wsData.getSessionToken().toString(), Utils.getEdgeMetadata(user.getRole()))); } /** diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java index f102730482d..458c2c18629 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketApi.java @@ -60,6 +60,8 @@ public class WebsocketApi extends AbstractOpenemsComponent public static final int DEFAULT_PORT = 8075; + private final static int POOL_SIZE = 10; + protected final ApiWorker apiWorker = new ApiWorker(this); private final SystemLogHandler systemLogHandler; @@ -111,7 +113,7 @@ protected void activate(ComponentContext context, Config config) { return; } this.apiWorker.setTimeoutSeconds(config.apiTimeout()); - this.startServer(config.port()); + this.startServer(config.port(), POOL_SIZE, false); } @Deactivate @@ -123,10 +125,12 @@ protected void deactivate() { /** * Create and start new server. * - * @param port the port + * @param port the port + * @param poolSize number of threads dedicated to handle the tasks + * @param debugMode activate a regular debug log about the state of the tasks */ - private synchronized void startServer(int port) { - this.server = new WebsocketServer(this, "Websocket Api", port); + private synchronized void startServer(int port, int poolSize, boolean debugMode) { + this.server = new WebsocketServer(this, "Websocket Api", port, poolSize, debugMode); this.server.start(); } diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java index b341142fc84..db347bd090b 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java @@ -13,8 +13,8 @@ public class WebsocketServer extends AbstractWebsocketServer { private final OnError onError; private final OnClose onClose; - public WebsocketServer(WebsocketApi parent, String name, int port) { - super(name, port); + public WebsocketServer(WebsocketApi parent, String name, int port, int poolSize, boolean debugMode) { + super(name, port, poolSize, debugMode); this.parent = parent; this.onOpen = new OnOpen(parent); this.onRequest = new OnRequest(parent); diff --git a/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java b/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java index cab60de764a..34e22a63c67 100644 --- a/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java +++ b/io.openems.edge.controller.api/src/io/openems/edge/controller/api/Controller.java @@ -4,6 +4,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.StateChannel; @@ -24,6 +25,7 @@ public interface Controller extends OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { RUN_FAILED(Doc.of(Level.FAULT) // + .persistencePriority(PersistencePriority.HIGH) // .text("Running the Controller failed")); private final Doc doc; diff --git a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java index b6d91486094..444ca211b65 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java @@ -31,6 +31,9 @@ import org.osgi.service.metatype.MetaTypeService; import org.slf4j.Logger; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + import io.openems.common.OpenemsConstants; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; @@ -312,11 +315,18 @@ protected CompletableFuture handleUpdateComponentConfigR for (Property property : request.getProperties()) { // do not allow certain properties to be updated, like pid and service.pid if (!EdgeConfig.ignorePropertyKey(property.getName())) { - Object value = JsonUtils.getAsBestType(property.getValue()); - if (value instanceof Object[] && ((Object[]) value).length == 0) { - value = new String[0]; + JsonElement jValue = property.getValue(); + if (jValue == null || jValue == JsonNull.INSTANCE) { + // Remove NULL property + properties.remove(property.getName()); + } else { + // Add updated Property + Object value = JsonUtils.getAsBestType(property.getValue()); + if (value instanceof Object[] && ((Object[]) value).length == 0) { + value = new String[0]; + } + properties.put(property.getName(), value); } - properties.put(property.getName(), value); } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/OsgiValidateWorker.java b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/OsgiValidateWorker.java index 1d5b341b577..63c1417943a 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/OsgiValidateWorker.java +++ b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/OsgiValidateWorker.java @@ -174,7 +174,17 @@ private static void updateInactiveComponentsUsingScr(Map defecti private static void updateInactiveComponentsUsingConfigurationAdmin(Map defectiveComponents, List enabledComponents, Configuration[] configs) { for (Configuration config : configs) { - Dictionary properties = config.getProperties(); + Dictionary properties; + try { + properties = config.getProperties(); + if (properties == null) { + // configuration was just created and update has not been called + continue; + } + } catch (IllegalStateException e) { + // Configuration has been deleted + continue; + } String componentId = (String) properties.get("id"); if (componentId != null) { if (defectiveComponents.containsKey(componentId)) { diff --git a/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/ExecuteSystemCommandRequest.java b/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/ExecuteSystemCommandRequest.java index e9b4634900b..6e36e0ee621 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/ExecuteSystemCommandRequest.java +++ b/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/ExecuteSystemCommandRequest.java @@ -91,7 +91,8 @@ public ExecuteSystemCommandRequest(String command, boolean runInBackground, int public ExecuteSystemCommandRequest(UUID id, String command, boolean runInBackground, int timeoutSeconds, Optional username, Optional password) { - super(id, METHOD); + super(id, METHOD, + timeoutSeconds + JsonrpcRequest.DEFAULT_TIMEOUT_SECONDS /* reuse timeoutSeconds with some buffer */); this.command = command; this.runInBackground = runInBackground; this.timeoutSeconds = timeoutSeconds; diff --git a/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/GetNetworkConfigRequest.java b/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/GetNetworkConfigRequest.java index bb4726f916e..b302d8937c1 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/GetNetworkConfigRequest.java +++ b/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/GetNetworkConfigRequest.java @@ -1,7 +1,5 @@ package io.openems.edge.core.host.jsonrpc; -import java.util.UUID; - import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; @@ -32,15 +30,15 @@ public class GetNetworkConfigRequest extends JsonrpcRequest { * @throws OpenemsNamedException on error */ public static GetNetworkConfigRequest from(JsonrpcRequest r) throws OpenemsException { - return new GetNetworkConfigRequest(r.getId()); + return new GetNetworkConfigRequest(r); } public GetNetworkConfigRequest() { - this(UUID.randomUUID()); + super(METHOD); } - public GetNetworkConfigRequest(UUID id) { - super(id, METHOD); + private GetNetworkConfigRequest(JsonrpcRequest request) { + super(request, METHOD); } @Override diff --git a/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/SetNetworkConfigRequest.java b/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/SetNetworkConfigRequest.java index cce6946bbfd..00fd4f774b1 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/SetNetworkConfigRequest.java +++ b/io.openems.edge.core/src/io/openems/edge/core/host/jsonrpc/SetNetworkConfigRequest.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; -import java.util.UUID; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -52,18 +51,19 @@ public static SetNetworkConfigRequest from(JsonrpcRequest r) throws OpenemsNamed for (Entry entry : jInterfaces.entrySet()) { interfaces.add(NetworkInterface.from(entry.getKey(), JsonUtils.getAsJsonObject(entry.getValue()))); } - return new SetNetworkConfigRequest(r.getId(), interfaces); + return new SetNetworkConfigRequest(r, interfaces); } private final List> networkInterfaces; public SetNetworkConfigRequest(List> interfaces) { - this(UUID.randomUUID(), interfaces); + super(METHOD); + this.networkInterfaces = interfaces; } - public SetNetworkConfigRequest(UUID id, List> networkInterfaces) { - super(id, METHOD); - this.networkInterfaces = networkInterfaces; + private SetNetworkConfigRequest(JsonrpcRequest request, List> interfaces) { + super(request, METHOD); + this.networkInterfaces = interfaces; } @Override diff --git a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionRequest.java b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionRequest.java index eb34b821b1e..2d09cdb307c 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionRequest.java +++ b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionRequest.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.UUID; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -31,6 +30,14 @@ public class Get24HoursPredictionRequest extends JsonrpcRequest { public static final String METHOD = "get24HoursPrediction"; + /** + * Create {@link Get24HoursPredictionRequest} from a template + * {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link Get24HoursPredictionRequest} + * @throws OpenemsNamedException on parse error + */ public static Get24HoursPredictionRequest from(JsonrpcRequest r) throws OpenemsNamedException { JsonObject p = r.getParams(); JsonArray cs = JsonUtils.getAsJsonArray(p, "channels"); @@ -38,17 +45,18 @@ public static Get24HoursPredictionRequest from(JsonrpcRequest r) throws OpenemsN for (JsonElement c : cs) { channels.add(ChannelAddress.fromString(JsonUtils.getAsString(c))); } - return new Get24HoursPredictionRequest(r.getId(), channels); + return new Get24HoursPredictionRequest(r, channels); } private final List channels; public Get24HoursPredictionRequest(List channels) { - this(UUID.randomUUID(), channels); + super(METHOD); + this.channels = channels; } - public Get24HoursPredictionRequest(UUID id, List channels) { - super(id, METHOD); + private Get24HoursPredictionRequest(JsonrpcRequest request, List channels) { + super(request, METHOD); this.channels = channels; } @@ -63,6 +71,11 @@ public JsonObject getParams() { .build(); } + /** + * Gets the {@link ChannelAddress}es. + * + * @return a list of {@link ChannelAddress} + */ public List getChannels() { return this.channels; } diff --git a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionResponse.java b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionResponse.java index cf13edaaf9a..674038be72d 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionResponse.java +++ b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/Get24HoursPredictionResponse.java @@ -50,8 +50,13 @@ public JsonObject getResult() { return j; } + /** + * Gets the {@link Prediction24Hours}s per {@link ChannelAddress}. + * + * @return a map of Predictions + */ public Map getPredictions() { - return predictions; + return this.predictions; } } diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java index ec9bac688e8..155e7511960 100644 --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/AsymmetricEss.java @@ -5,6 +5,7 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; @@ -30,6 +31,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -44,6 +46,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -58,6 +61,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -72,6 +76,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -86,6 +91,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -100,6 +106,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ); @@ -151,7 +158,7 @@ public default Value getActivePowerL1() { public default void _setActivePowerL1(Integer value) { this.getActivePowerL1Channel().setNextValue(value); } - + /** * Internal method to set the 'nextValue' on {@link ChannelId#ACTIVE_POWER_L1} * Channel. @@ -190,7 +197,7 @@ public default Value getActivePowerL2() { public default void _setActivePowerL2(Integer value) { this.getActivePowerL2Channel().setNextValue(value); } - + /** * Internal method to set the 'nextValue' on {@link ChannelId#ACTIVE_POWER_L2} * Channel. @@ -229,7 +236,7 @@ public default Value getActivePowerL3() { public default void _setActivePowerL3(Integer value) { this.getActivePowerL3Channel().setNextValue(value); } - + /** * Internal method to set the 'nextValue' on {@link ChannelId#ACTIVE_POWER_L3} * Channel. @@ -268,7 +275,7 @@ public default Value getReactivePowerL1() { public default void _setReactivePowerL1(Integer value) { this.getReactivePowerL1Channel().setNextValue(value); } - + /** * Internal method to set the 'nextValue' on {@link ChannelId#REACTIVE_POWER_L1} * Channel. @@ -307,7 +314,7 @@ public default Value getReactivePowerL2() { public default void _setReactivePowerL2(Integer value) { this.getReactivePowerL2Channel().setNextValue(value); } - + /** * Internal method to set the 'nextValue' on {@link ChannelId#REACTIVE_POWER_L2} * Channel. @@ -346,7 +353,7 @@ public default Value getReactivePowerL3() { public default void _setReactivePowerL3(Integer value) { this.getReactivePowerL3Channel().setNextValue(value); } - + /** * Internal method to set the 'nextValue' on {@link ChannelId#REACTIVE_POWER_L3} * Channel. diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/HybridEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/HybridEss.java index 641af12230e..681068beb1a 100644 --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/HybridEss.java +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/HybridEss.java @@ -2,6 +2,7 @@ import org.osgi.annotation.versioning.ProviderType; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; @@ -34,6 +35,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ DC_DISCHARGE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -46,7 +48,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DC_CHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)), // /** * DC Discharge Energy. * @@ -57,7 +60,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DC_DISCHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)),; + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)); // private final Doc doc; diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java index 217a68528d8..971cc7655eb 100644 --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedAsymmetricEss.java @@ -3,6 +3,7 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.OpenemsType; @@ -299,7 +300,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DEBUG_SET_ACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Holds settings of Reactive Power for debugging * @@ -314,7 +316,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DEBUG_SET_REACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE_REACTIVE)), // + .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Holds settings of Active Power L2 for debugging * @@ -329,7 +332,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DEBUG_SET_ACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Holds settings of Reactive Power for debugging * @@ -344,7 +348,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DEBUG_SET_REACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE_REACTIVE)), // + .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Holds settings of Active Power L1 for debugging * @@ -359,7 +364,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DEBUG_SET_ACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Holds settings of Reactive Power for debugging * @@ -374,8 +380,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ DEBUG_SET_REACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE_REACTIVE)), // - ; + .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH)); // private final Doc doc; diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java index 535e54ccabf..2967d1071dd 100644 --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/ManagedSymmetricEss.java @@ -4,6 +4,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; @@ -40,7 +41,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ALLOWED_CHARGE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Holds the currently maximum allowed discharge power. This value is commonly * defined by current battery limitations. @@ -53,7 +55,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ALLOWED_DISCHARGE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Sets a fixed Active Power. * @@ -193,7 +196,8 @@ public void accept(Channel channel) { * */ DEBUG_SET_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Holds settings of Reactive Power for debugging. * @@ -207,7 +211,8 @@ public void accept(Channel channel) { * */ DEBUG_SET_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE_REACTIVE)), // + .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * StateChannel is set when calling applyPower() failed. * @@ -220,6 +225,7 @@ public void accept(Channel channel) { * */ APPLY_POWER_FAILED(Doc.of(Level.FAULT) // + .persistencePriority(PersistencePriority.HIGH) // .text("Applying the Active/Reactive Power failed")); private final Doc doc; diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java index c43f541c938..0793c9841cf 100644 --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/api/SymmetricEss.java @@ -3,6 +3,7 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; @@ -32,7 +33,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ SOC(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT)), + .unit(Unit.PERCENT) // + .persistencePriority(PersistencePriority.HIGH)), /** * Capacity. * @@ -45,7 +47,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * @since 2019.5.0 */ CAPACITY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)), /** * Grid-Mode. * @@ -55,7 +58,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *

  • Range: 0=Undefined, 1=On-Grid, 2=Off-Grid * */ - GRID_MODE(Doc.of(GridMode.values())), + GRID_MODE(Doc.of(GridMode.values()) // + .persistencePriority(PersistencePriority.HIGH)), /** * Active Power. * @@ -68,6 +72,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -82,6 +87,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT) // ), /** @@ -96,7 +102,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ MAX_APPARENT_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE)), // + .unit(Unit.VOLT_AMPERE) // + .persistencePriority(PersistencePriority.HIGH)), /** * Active Charge Energy. * @@ -107,7 +114,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ACTIVE_CHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)), /** * Active Discharge Energy. * @@ -118,7 +126,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ACTIVE_DISCHARGE_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)), /** * Min Cell Voltage. * @@ -133,7 +142,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MIN_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - ), + .persistencePriority(PersistencePriority.HIGH)), /** * Max Cell Voltage. * @@ -148,7 +157,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MAX_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIVOLT) // - ), + .persistencePriority(PersistencePriority.HIGH)), /** * Min Cell Temperature. * @@ -163,7 +172,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MIN_CELL_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.DEGREE_CELSIUS) // - ), + .persistencePriority(PersistencePriority.HIGH)), /** * Max Cell Temperature. * @@ -178,7 +187,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MAX_CELL_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // .unit(Unit.DEGREE_CELSIUS) // - ); + .persistencePriority(PersistencePriority.HIGH)); private final Doc doc; diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java index 0cd967dfd01..ebd069b2af0 100644 --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/dccharger/api/EssDcCharger.java @@ -2,6 +2,7 @@ import org.osgi.annotation.versioning.ProviderType; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.common.utils.IntUtils; @@ -28,7 +29,9 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Implementation Note: value is automatically derived from ACTUAL_POWER * */ - MAX_ACTUAL_POWER(Doc.of(OpenemsType.INTEGER).unit(Unit.WATT)), // + MAX_ACTUAL_POWER(Doc.of(OpenemsType.INTEGER)// + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Actual Power * @@ -39,24 +42,28 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Range: positive * */ - ACTUAL_POWER(Doc.of(OpenemsType.INTEGER).unit(Unit.WATT).onInit(channel -> { - channel.onSetNextValue(value -> { - /* - * Fill Max Actual Power channel - */ - if (value.asOptional().isPresent()) { - int newValue = (int) (Integer) value.get(); - Channel maxActualPowerChannel = channel.getComponent().channel(ChannelId.MAX_ACTUAL_POWER); - int maxActualPower = maxActualPowerChannel.value().orElse(0); - int maxNextActualPower = maxActualPowerChannel.getNextValue().orElse(0); - if (newValue > Math.max(maxActualPower, maxNextActualPower)) { - // avoid getting called too often -> round to 100 - newValue = IntUtils.roundToPrecision(newValue, Round.AWAY_FROM_ZERO, 100); - maxActualPowerChannel.setNextValue(newValue); - } - } - }); - })), + ACTUAL_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // + .onInit(channel -> { + channel.onSetNextValue(value -> { + /* + * Fill Max Actual Power channel + */ + if (value.asOptional().isPresent()) { + int newValue = (int) (Integer) value.get(); + Channel maxActualPowerChannel = channel.getComponent() + .channel(ChannelId.MAX_ACTUAL_POWER); + int maxActualPower = maxActualPowerChannel.value().orElse(0); + int maxNextActualPower = maxActualPowerChannel.getNextValue().orElse(0); + if (newValue > Math.max(maxActualPower, maxNextActualPower)) { + // avoid getting called too often -> round to 100 + newValue = IntUtils.roundToPrecision(newValue, Round.AWAY_FROM_ZERO, 100); + maxActualPowerChannel.setNextValue(newValue); + } + } + }); + })), /** * Actual Energy * @@ -67,7 +74,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ACTUAL_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)); + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)); // private final Doc doc; diff --git a/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java b/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java index 0aea023b6f1..b651a05567a 100644 --- a/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java +++ b/io.openems.edge.ess.api/src/io/openems/edge/ess/power/api/SolverStrategy.java @@ -21,12 +21,12 @@ private SolverStrategy(int value, String name) { @Override public int getValue() { - return value; + return this.value; } @Override public String getName() { - return name; + return this.name; } @Override diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AbstractGenericEssChannelManager.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AbstractGenericEssChannelManager.java new file mode 100644 index 00000000000..68c9210389e --- /dev/null +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AbstractGenericEssChannelManager.java @@ -0,0 +1,96 @@ +package io.openems.edge.ess.generic.common; + +import io.openems.edge.battery.api.Battery; +import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; +import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter; +import io.openems.edge.common.channel.AbstractChannelListenerManager; +import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.channel.ChannelId; +import io.openems.edge.common.component.ClockProvider; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.ess.api.SymmetricEss; + +/** + * Helper wrapping class to handle everything related to Channels; in particular + * calculating the Ess-Channels based on the Channels of the Battery and + * Battery-Inverter. Takes care of registering and unregistering listeners. + */ +public class AbstractGenericEssChannelManager + extends AbstractChannelListenerManager { + + private final GenericManagedEss parent; + private final AllowedChargeDischargeHandler allowedChargeDischargeHandler; + + public AbstractGenericEssChannelManager(GenericManagedEss parent) { + this.parent = parent; + this.allowedChargeDischargeHandler = new AllowedChargeDischargeHandler(parent); + } + + /** + * Called on Component activate(). + * + * @param clockProvider the {@link ClockProvider} + * @param battery the {@link Battery} + * @param batteryInverter the {@link ManagedSymmetricBatteryInverter} + */ + public void activate(ClockProvider clockProvider, Battery battery, + ManagedSymmetricBatteryInverter batteryInverter) { + /* + * Battery + */ + this.addOnSetNextValueListener(battery, Battery.ChannelId.DISCHARGE_MIN_VOLTAGE, + (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); + this.addOnSetNextValueListener(battery, Battery.ChannelId.DISCHARGE_MAX_CURRENT, + (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); + this.addOnSetNextValueListener(battery, Battery.ChannelId.CHARGE_MAX_VOLTAGE, + (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); + this.addOnSetNextValueListener(battery, Battery.ChannelId.CHARGE_MAX_CURRENT, + (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); + this.addCopyListener(battery, // + Battery.ChannelId.CAPACITY, // + SymmetricEss.ChannelId.CAPACITY); + this.addCopyListener(battery, // + Battery.ChannelId.SOC, // + SymmetricEss.ChannelId.SOC); + + /* + * Battery-Inverter + */ + this.addCopyListener(batteryInverter, // + SymmetricBatteryInverter.ChannelId.ACTIVE_CHARGE_ENERGY, // + SymmetricEss.ChannelId.ACTIVE_CHARGE_ENERGY); + this.addCopyListener(batteryInverter, // + SymmetricBatteryInverter.ChannelId.ACTIVE_DISCHARGE_ENERGY, // + SymmetricEss.ChannelId.ACTIVE_DISCHARGE_ENERGY); + this.addCopyListener(batteryInverter, // + SymmetricBatteryInverter.ChannelId.ACTIVE_POWER, // + SymmetricEss.ChannelId.ACTIVE_POWER); + this.addCopyListener(batteryInverter, // + SymmetricBatteryInverter.ChannelId.GRID_MODE, // + SymmetricEss.ChannelId.GRID_MODE); + this.addCopyListener(batteryInverter, // + SymmetricBatteryInverter.ChannelId.MAX_APPARENT_POWER, // + SymmetricEss.ChannelId.MAX_APPARENT_POWER); + this.addCopyListener(batteryInverter, // + SymmetricBatteryInverter.ChannelId.REACTIVE_POWER, // + SymmetricEss.ChannelId.REACTIVE_POWER); + } + + /** + * Adds a Copy-Listener. It listens on setNextValue() and copies the value to + * the target channel. + * + * @param the Channel-Type + * @param sourceComponent the source component - Battery or BatteryInverter + * @param sourceChannelId the source ChannelId + * @param targetChannelId the target ChannelId + */ + private void addCopyListener(OpenemsComponent sourceComponent, ChannelId sourceChannelId, + ChannelId targetChannelId) { + this.addOnSetNextValueListener(sourceComponent, sourceChannelId, (value) -> { + Channel targetChannel = this.parent.channel(targetChannelId); + targetChannel.setNextValue(value); + }); + } + +} diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AbstractGenericManagedEss.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AbstractGenericManagedEss.java new file mode 100644 index 00000000000..095126d4580 --- /dev/null +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AbstractGenericManagedEss.java @@ -0,0 +1,237 @@ +package io.openems.edge.ess.generic.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.channel.AccessMode; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.battery.api.Battery; +import io.openems.edge.batteryinverter.api.BatteryInverterConstraint; +import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; +import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter; +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.modbusslave.ModbusSlave; +import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.startstop.StartStop; +import io.openems.edge.common.startstop.StartStopConfig; +import io.openems.edge.common.startstop.StartStoppable; +import io.openems.edge.ess.api.ManagedSymmetricEss; +import io.openems.edge.ess.api.SymmetricEss; +import io.openems.edge.ess.generic.common.statemachine.Context; +import io.openems.edge.ess.generic.common.statemachine.StateMachine; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; +import io.openems.edge.ess.power.api.Constraint; +import io.openems.edge.ess.power.api.Phase; +import io.openems.edge.ess.power.api.Pwr; +import io.openems.edge.ess.power.api.Relationship; + +/** + * Parent class for different implementations of Managed Energy Storage Systems, + * consisting of a Battery-Inverter component and a Battery component. + */ +public abstract class AbstractGenericManagedEss + extends AbstractOpenemsComponent implements GenericManagedEss, ManagedSymmetricEss, SymmetricEss, + OpenemsComponent, EventHandler, StartStoppable, ModbusSlave { + + private final Logger log = LoggerFactory.getLogger(AbstractGenericManagedEss.class); + + /** + * Manages the {@link State}s of the StateMachine. + */ + private final StateMachine stateMachine = new StateMachine(State.UNDEFINED); + + /** + * Helper wrapping class to handle everything related to Channels. + * + * @return the {@link AbstractGenericEssChannelManager} + */ + protected abstract AbstractGenericEssChannelManager getChannelManager(); + + protected abstract ComponentManager getComponentManager(); + + protected abstract BATTERY getBattery(); + + protected abstract BATTERY_INVERTER getBatteryInverter(); + + private StartStopConfig startStopConfig = StartStopConfig.AUTO; + + protected AbstractGenericManagedEss(io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { + super(firstInitialChannelIds, furtherInitialChannelIds); + } + + @Override + protected void activate(ComponentContext context, String id, String alias, boolean enabled) { + throw new IllegalArgumentException("Use the other activate() method!"); + } + + protected void activate(ComponentContext context, String id, String alias, boolean enabled, + StartStopConfig startStopConfig, ConfigurationAdmin cm, String batteryInverterId, String batteryId) { + super.activate(context, id, alias, enabled); + this.startStopConfig = startStopConfig; + + // update filter for 'BatteryInverter' + if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "batteryInverter", batteryInverterId)) { + return; + } + + // update filter for 'Battery' + if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "battery", batteryId)) { + return; + } + + this.getChannelManager().activate(this.getComponentManager(), this.getBattery(), this.getBatteryInverter()); + } + + protected void deactivate() { + this.getChannelManager().deactivate(); + super.deactivate(); + } + + @Override + public void handleEvent(Event event) { + if (!this.isEnabled()) { + return; + } + switch (event.getTopic()) { + + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: + this.handleStateMachine(); + break; + } + } + + /** + * Handles the State-Machine. + */ + private void handleStateMachine() { + // Store the current State + this.channel(GenericManagedEss.ChannelId.STATE_MACHINE).setNextValue(this.stateMachine.getCurrentState()); + + // Initialize 'Start-Stop' Channel + this._setStartStop(StartStop.UNDEFINED); + + // Prepare Context + Context context = new Context(this, this.getBattery(), this.getBatteryInverter()); + + // Call the StateMachine + try { + this.stateMachine.run(context); + + this.channel(GenericManagedEss.ChannelId.RUN_FAILED).setNextValue(false); + + } catch (OpenemsNamedException e) { + this.channel(GenericManagedEss.ChannelId.RUN_FAILED).setNextValue(true); + this.logError(this.log, "StateMachine failed: " + e.getMessage()); + } + } + + @Override + public String debugLog() { + return "SoC:" + this.getSoc().asString() // + + "|L:" + this.getActivePower().asString() // + + "|Allowed:" // + + this.getAllowedChargePower().asStringWithoutUnit() + ";" // + + this.getAllowedDischargePower().asString() // + + "|" + this.channel(GenericManagedEss.ChannelId.STATE_MACHINE).value().asOptionString(); + } + + /** + * Forwards the power request to the {@link SymmetricBatteryInverter}. + * + * {@inheritDoc} + */ + @Override + public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException { + this.getBatteryInverter().run(this.getBattery(), activePower, reactivePower); + } + + /** + * Retrieves PowerPrecision from {@link SymmetricBatteryInverter}. + * + * {@inheritDoc} + */ + @Override + public int getPowerPrecision() { + return this.getBatteryInverter().getPowerPrecision(); + } + + /** + * Retrieves StaticConstraints from {@link SymmetricBatteryInverter}. + * + * {@inheritDoc} + */ + @Override + public Constraint[] getStaticConstraints() throws OpenemsNamedException { + + List result = new ArrayList(); + + // Get BatteryInverterConstraints + BatteryInverterConstraint[] constraints = this.getBatteryInverter().getStaticConstraints(); + + for (int i = 0; i < constraints.length; i++) { + BatteryInverterConstraint c = constraints[i]; + result.add(this.getPower().createSimpleConstraint(c.description, this, c.phase, c.pwr, c.relationship, + c.value)); + } + + // If the GenericEss is not in State "STARTED" block ACTIVE and REACTIVE Power! + if (!this.isStarted()) { + result.add(this.createPowerConstraint("ActivePower Constraint ESS not Started", Phase.ALL, Pwr.ACTIVE, + Relationship.EQUALS, 0)); + result.add(this.createPowerConstraint("ReactivePower Constraint ESS not Started", Phase.ALL, Pwr.REACTIVE, + Relationship.EQUALS, 0)); + } + return result.toArray(new Constraint[result.size()]); + } + + private AtomicReference startStopTarget = new AtomicReference(StartStop.UNDEFINED); + + @Override + public void setStartStop(StartStop value) { + if (this.startStopTarget.getAndSet(value) != value) { + // Set only if value changed + this.stateMachine.forceNextState(State.UNDEFINED); + } + } + + @Override + public StartStop getStartStopTarget() { + switch (this.startStopConfig) { + case AUTO: + // read StartStop-Channel + return this.startStopTarget.get(); + + case START: + // force START + return StartStop.START; + + case STOP: + // force STOP + return StartStop.STOP; + } + + assert false; + return StartStop.UNDEFINED; // can never happen + } + + @Override + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { + return new ModbusSlaveTable(// + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricEss.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode) // + ); + } +} diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/AllowedChargeDischargeHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AllowedChargeDischargeHandler.java similarity index 94% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/AllowedChargeDischargeHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AllowedChargeDischargeHandler.java index 0b121b7b5ca..7bc859f05ac 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/AllowedChargeDischargeHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/AllowedChargeDischargeHandler.java @@ -1,4 +1,4 @@ -package io.openems.edge.ess.generic.symmetric; +package io.openems.edge.ess.generic.common; import java.time.Duration; import java.time.Instant; @@ -9,6 +9,7 @@ import io.openems.edge.common.component.ClockProvider; import io.openems.edge.common.startstop.StartStoppable; import io.openems.edge.ess.api.ManagedSymmetricEss; +import io.openems.edge.ess.generic.symmetric.ChannelManager; /** * Helper class to handle calculation of Allowed-Charge-Power and @@ -17,7 +18,7 @@ */ public class AllowedChargeDischargeHandler implements BiConsumer { - public final static float DISCHARGE_EFFICIENCY_FACTOR = 0.95F; + public static final float DISCHARGE_EFFICIENCY_FACTOR = 0.95F; /** * Allow a maximum increase per second. @@ -25,7 +26,7 @@ public class AllowedChargeDischargeHandler implements BiConsumer * 5 % of possible allowed charge/discharge power */ - public final static float MAX_INCREASE_PERCENTAGE = 0.05F; + public static final float MAX_INCREASE_PERCENTAGE = 0.05F; private final ManagedSymmetricEss parent; @@ -63,6 +64,7 @@ public void accept(ClockProvider clockProvider, Battery battery) { * parameters. Result is stored in 'allowedChargePower' and * 'allowedDischargePower' variables - both as positive values! * + * @param clockProvider the {@link ClockProvider} * @param isStarted is the ESS started? * @param chargeMaxCurrent the {@link Battery.ChannelId#CHARGE_MAX_CURRENT} * @param dischargeMaxCurrent the {@link Battery.ChannelId#DISHARGE_MAX_CURRENT} diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEss.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/GenericManagedEss.java similarity index 93% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEss.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/GenericManagedEss.java index 3835f47c977..18f60f41b6a 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEss.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/GenericManagedEss.java @@ -1,4 +1,4 @@ -package io.openems.edge.ess.generic.symmetric; +package io.openems.edge.ess.generic.common; import io.openems.common.channel.AccessMode; import io.openems.common.channel.Level; @@ -14,9 +14,9 @@ import io.openems.edge.common.startstop.StartStoppable; import io.openems.edge.ess.api.ManagedSymmetricEss; import io.openems.edge.ess.api.SymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; -public interface GenericManagedSymmetricEss extends ManagedSymmetricEss, StartStoppable, ModbusSlave { +public interface GenericManagedEss extends ManagedSymmetricEss, StartStoppable, ModbusSlave { /** * Efficiency factor to calculate AC Charge/Discharge limits from DC. Used at @@ -68,8 +68,8 @@ public default ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { SymmetricEss.getModbusSlaveNatureTable(accessMode), // ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode), // StartStoppable.getModbusSlaveNatureTable(accessMode), // - ModbusSlaveNatureTable.of(GenericManagedSymmetricEss.class, accessMode, 100) // - .channel(0, GenericManagedSymmetricEss.ChannelId.STATE_MACHINE, ModbusType.UINT16) // + ModbusSlaveNatureTable.of(GenericManagedEss.class, accessMode, 100) // + .channel(0, GenericManagedEss.ChannelId.STATE_MACHINE, ModbusType.UINT16) // .build()); } diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/package-info.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/package-info.java similarity index 77% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/package-info.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/package-info.java index ab8586d01ed..1fd7a5acc1b 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/package-info.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/package-info.java @@ -1,4 +1,4 @@ @org.osgi.annotation.versioning.Version("1.0.0") @org.osgi.annotation.bundle.Export // TODO remove, once Ess-Sinexcel is migrated to Battery-Inverter; see #1389 -package io.openems.edge.ess.generic.symmetric; +package io.openems.edge.ess.generic.common; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/Context.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/Context.java new file mode 100644 index 00000000000..a41bb06644e --- /dev/null +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/Context.java @@ -0,0 +1,18 @@ +package io.openems.edge.ess.generic.common.statemachine; + +import io.openems.edge.battery.api.Battery; +import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; +import io.openems.edge.common.statemachine.AbstractContext; +import io.openems.edge.ess.generic.common.GenericManagedEss; + +public class Context extends AbstractContext { + + protected final Battery battery; + protected final ManagedSymmetricBatteryInverter batteryInverter; + + public Context(GenericManagedEss parent, Battery battery, ManagedSymmetricBatteryInverter batteryInverter) { + super(parent); + this.battery = battery; + this.batteryInverter = batteryInverter; + } +} \ No newline at end of file diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/ErrorHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/ErrorHandler.java similarity index 80% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/ErrorHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/ErrorHandler.java index 3cdba1a9e36..fec12f30de1 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/ErrorHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/ErrorHandler.java @@ -1,4 +1,4 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import java.time.Duration; import java.time.Instant; @@ -6,8 +6,8 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.startstop.StartStop; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class ErrorHandler extends StateHandler { @@ -24,7 +24,7 @@ protected void onEntry(Context context) throws OpenemsNamedException { @Override protected void onExit(Context context) throws OpenemsNamedException { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); ess._setMaxBatteryStartAttemptsFault(false); ess._setMaxBatteryStopAttemptsFault(false); diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartBatteryHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartBatteryHandler.java similarity index 73% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartBatteryHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartBatteryHandler.java index e693b9670de..6f2579f6490 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartBatteryHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartBatteryHandler.java @@ -1,12 +1,12 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import java.time.Duration; import java.time.Instant; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class StartBatteryHandler extends StateHandler { @@ -17,13 +17,13 @@ public class StartBatteryHandler extends StateHandler { protected void onEntry(Context context) throws OpenemsNamedException { this.lastAttempt = Instant.MIN; this.attemptCounter = 0; - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); ess._setMaxBatteryStartAttemptsFault(false); } @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); if (context.battery.isStarted()) { // TODO should we check here the other parameters defined in Battery Nature. @@ -31,11 +31,11 @@ public State runAndGetNextState(Context context) throws OpenemsNamedException { } boolean isMaxStartTimePassed = Duration.between(this.lastAttempt, Instant.now()) - .getSeconds() > GenericManagedSymmetricEss.RETRY_COMMAND_SECONDS; + .getSeconds() > GenericManagedEss.RETRY_COMMAND_SECONDS; if (isMaxStartTimePassed) { // First try - or waited long enough for next try - if (this.attemptCounter > GenericManagedSymmetricEss.RETRY_COMMAND_MAX_ATTEMPTS) { + if (this.attemptCounter > GenericManagedEss.RETRY_COMMAND_MAX_ATTEMPTS) { // Too many tries ess._setMaxBatteryStartAttemptsFault(true); return State.UNDEFINED; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartBatteryInverterHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartBatteryInverterHandler.java similarity index 72% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartBatteryInverterHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartBatteryInverterHandler.java index b8b1ea2beeb..9887a0e3b5b 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartBatteryInverterHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartBatteryInverterHandler.java @@ -1,12 +1,12 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import java.time.Duration; import java.time.Instant; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class StartBatteryInverterHandler extends StateHandler { @@ -17,24 +17,24 @@ public class StartBatteryInverterHandler extends StateHandler { protected void onEntry(Context context) throws OpenemsNamedException { this.lastAttempt = Instant.MIN; this.attemptCounter = 0; - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); ess._setMaxBatteryInverterStartAttemptsFault(false); } @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); if (context.batteryInverter.isStarted()) { return State.STARTED; } boolean isMaxStartTimePassed = Duration.between(this.lastAttempt, Instant.now()) - .getSeconds() > GenericManagedSymmetricEss.RETRY_COMMAND_SECONDS; + .getSeconds() > GenericManagedEss.RETRY_COMMAND_SECONDS; if (isMaxStartTimePassed) { // First try - or waited long enough for next try - if (this.attemptCounter > GenericManagedSymmetricEss.RETRY_COMMAND_MAX_ATTEMPTS) { + if (this.attemptCounter > GenericManagedEss.RETRY_COMMAND_MAX_ATTEMPTS) { // Too many tries ess._setMaxBatteryInverterStartAttemptsFault(true); return State.UNDEFINED; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartedHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartedHandler.java similarity index 66% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartedHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartedHandler.java index 9c25fa222cb..3d50b8f7468 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StartedHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StartedHandler.java @@ -1,15 +1,15 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import io.openems.edge.common.startstop.StartStop; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class StartedHandler extends StateHandler { @Override public State runAndGetNextState(Context context) { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); if (ess.hasFaults()) { return State.UNDEFINED; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StateMachine.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StateMachine.java similarity index 96% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StateMachine.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StateMachine.java index 166e52771cf..70da6405b36 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StateMachine.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StateMachine.java @@ -1,4 +1,4 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import com.google.common.base.CaseFormat; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StopBatteryHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StopBatteryHandler.java similarity index 71% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StopBatteryHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StopBatteryHandler.java index b1333dfd8f3..e6f2af40d67 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StopBatteryHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StopBatteryHandler.java @@ -1,12 +1,12 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import java.time.Duration; import java.time.Instant; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class StopBatteryHandler extends StateHandler { @@ -17,24 +17,24 @@ public class StopBatteryHandler extends StateHandler { protected void onEntry(Context context) throws OpenemsNamedException { this.lastAttempt = Instant.MIN; this.attemptCounter = 0; - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); ess._setMaxBatteryStopAttemptsFault(false); } @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); if (context.battery.isStopped()) { return State.STOPPED; } boolean isMaxStartTimePassed = Duration.between(this.lastAttempt, Instant.now()) - .getSeconds() > GenericManagedSymmetricEss.RETRY_COMMAND_SECONDS; + .getSeconds() > GenericManagedEss.RETRY_COMMAND_SECONDS; if (isMaxStartTimePassed) { // First try - or waited long enough for next try - if (this.attemptCounter > GenericManagedSymmetricEss.RETRY_COMMAND_MAX_ATTEMPTS) { + if (this.attemptCounter > GenericManagedEss.RETRY_COMMAND_MAX_ATTEMPTS) { // Too many tries ess._setMaxBatteryStopAttemptsFault(true); return State.UNDEFINED; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StopBatteryInverterHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StopBatteryInverterHandler.java similarity index 72% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StopBatteryInverterHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StopBatteryInverterHandler.java index ea8f392a0ff..3aebdb36bac 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StopBatteryInverterHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StopBatteryInverterHandler.java @@ -1,12 +1,12 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import java.time.Duration; import java.time.Instant; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class StopBatteryInverterHandler extends StateHandler { @@ -17,24 +17,24 @@ public class StopBatteryInverterHandler extends StateHandler { protected void onEntry(Context context) throws OpenemsNamedException { this.lastAttempt = Instant.MIN; this.attemptCounter = 0; - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); ess._setMaxBatteryInverterStopAttemptsFault(false); } @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); if (context.batteryInverter.isStopped()) { return State.STOP_BATTERY; } boolean isMaxStartTimePassed = Duration.between(this.lastAttempt, Instant.now()) - .getSeconds() > GenericManagedSymmetricEss.RETRY_COMMAND_SECONDS; + .getSeconds() > GenericManagedEss.RETRY_COMMAND_SECONDS; if (isMaxStartTimePassed) { // First try - or waited long enough for next try - if (this.attemptCounter > GenericManagedSymmetricEss.RETRY_COMMAND_MAX_ATTEMPTS) { + if (this.attemptCounter > GenericManagedEss.RETRY_COMMAND_MAX_ATTEMPTS) { // Too many tries ess._setMaxBatteryInverterStopAttemptsFault(true); return State.UNDEFINED; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StoppedHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StoppedHandler.java similarity index 66% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StoppedHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StoppedHandler.java index 41d3178477a..3c107166071 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/StoppedHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/StoppedHandler.java @@ -1,15 +1,15 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import io.openems.edge.common.startstop.StartStop; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class StoppedHandler extends StateHandler { @Override public State runAndGetNextState(Context context) { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); if (ess.hasFaults()) { return State.UNDEFINED; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/UndefinedHandler.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/UndefinedHandler.java similarity index 75% rename from io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/UndefinedHandler.java rename to io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/UndefinedHandler.java index 6ed6f647c71..42c54a09980 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/UndefinedHandler.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/common/statemachine/UndefinedHandler.java @@ -1,14 +1,14 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; +package io.openems.edge.ess.generic.common.statemachine; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; public class UndefinedHandler extends StateHandler { @Override public State runAndGetNextState(Context context) { - GenericManagedSymmetricEss ess = context.getParent(); + GenericManagedEss ess = context.getParent(); switch (ess.getStartStopTarget()) { case UNDEFINED: // Stuck in UNDEFINED State diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/ChannelManager.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/ChannelManager.java new file mode 100644 index 00000000000..cf42e11c884 --- /dev/null +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/ChannelManager.java @@ -0,0 +1,14 @@ +package io.openems.edge.ess.generic.hybrid; + +import io.openems.edge.battery.api.Battery; +import io.openems.edge.batteryinverter.api.HybridManagedSymmetricBatteryInverter; +import io.openems.edge.ess.generic.common.AbstractGenericEssChannelManager; +import io.openems.edge.ess.generic.common.GenericManagedEss; + +public class ChannelManager extends AbstractGenericEssChannelManager { + + public ChannelManager(GenericManagedEss parent) { + super(parent); + } + +} diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/Config.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/Config.java new file mode 100644 index 00000000000..bb8f9e4c908 --- /dev/null +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/Config.java @@ -0,0 +1,39 @@ +package io.openems.edge.ess.generic.hybrid; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.edge.common.startstop.StartStopConfig; + +@ObjectClassDefinition(// + name = "ESS Hybrid Generic Managed Symmetric", // + description = "") +@interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "ess0"; + + @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 = "Start/stop behaviour?", description = "Should this Component be forced to start or stop?") + StartStopConfig startStop() default StartStopConfig.START; + + @AttributeDefinition(name = "Battery-Inverter-ID", description = "ID of Battery-Inverter.") + String batteryInverter_id() default "batteryInverter0"; + + @AttributeDefinition(name = "Battery-ID", description = "ID of Battery.") + String battery_id() default "battery0"; + + @AttributeDefinition(name = "Battery-Inverter target filter", description = "This is auto-generated by 'Battery-Inverter-ID'.") + String batteryInverter_target() default ""; + + @AttributeDefinition(name = "Battery target filter", description = "This is auto-generated by 'Battery-ID'.") + String battery_target() default ""; + + String webconsole_configurationFactory_nameHint() default "ESS Hybrid Generic Managed Symmetric [{id}]"; + +} \ No newline at end of file diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/GenericHybridManagedSymmetricEssImpl.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/GenericHybridManagedSymmetricEssImpl.java new file mode 100644 index 00000000000..5d18cdc8065 --- /dev/null +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/hybrid/GenericHybridManagedSymmetricEssImpl.java @@ -0,0 +1,120 @@ +package io.openems.edge.ess.generic.hybrid; + +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.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.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.metatype.annotations.Designate; + +import io.openems.edge.battery.api.Battery; +import io.openems.edge.batteryinverter.api.HybridManagedSymmetricBatteryInverter; +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.modbusslave.ModbusSlave; +import io.openems.edge.common.startstop.StartStoppable; +import io.openems.edge.ess.api.HybridEss; +import io.openems.edge.ess.api.ManagedSymmetricEss; +import io.openems.edge.ess.api.SymmetricEss; +import io.openems.edge.ess.generic.common.AbstractGenericManagedEss; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.power.api.Power; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "Ess.Generic.HybridManagedSymmetric", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE, // + property = { // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // + } // +) +public class GenericHybridManagedSymmetricEssImpl + extends AbstractGenericManagedEss + implements HybridEss, GenericManagedEss, ManagedSymmetricEss, SymmetricEss, OpenemsComponent, EventHandler, + StartStoppable, ModbusSlave { + + @Reference + private Power power; + + @Reference + private ConfigurationAdmin cm; + + @Reference + private ComponentManager componentManager; + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + private HybridManagedSymmetricBatteryInverter batteryInverter; + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + private Battery battery; + + private final ChannelManager channelManager = new ChannelManager(this); + + public GenericHybridManagedSymmetricEssImpl() { + super(// + OpenemsComponent.ChannelId.values(), // + StartStoppable.ChannelId.values(), // + SymmetricEss.ChannelId.values(), // + ManagedSymmetricEss.ChannelId.values(), // + GenericManagedEss.ChannelId.values(), // + HybridEss.ChannelId.values() // + ); + } + + @Activate + void activate(ComponentContext context, Config config) { + super.activate(context, config.id(), config.alias(), config.enabled(), config.startStop(), this.cm, + config.batteryInverter_id(), config.battery_id()); + } + + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + public void handleEvent(Event event) { + super.handleEvent(event); + } + + @Override + public Power getPower() { + return this.power; + } + + @Override + protected ChannelManager getChannelManager() { + return this.channelManager; + } + + @Override + protected Battery getBattery() { + return this.battery; + } + + @Override + protected HybridManagedSymmetricBatteryInverter getBatteryInverter() { + return this.batteryInverter; + } + + @Override + public Integer getSurplusPower() { + return this.batteryInverter.getSurplusPower(); + } + + @Override + protected ComponentManager getComponentManager() { + return this.componentManager; + } + +} diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/ChannelManager.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/ChannelManager.java index fa47f02cefb..e43858b9948 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/ChannelManager.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/ChannelManager.java @@ -2,94 +2,13 @@ import io.openems.edge.battery.api.Battery; import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; -import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter; -import io.openems.edge.common.channel.AbstractChannelListenerManager; -import io.openems.edge.common.channel.Channel; -import io.openems.edge.common.channel.ChannelId; -import io.openems.edge.common.component.ClockProvider; -import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.ess.api.SymmetricEss; +import io.openems.edge.ess.generic.common.AbstractGenericEssChannelManager; +import io.openems.edge.ess.generic.common.GenericManagedEss; -/** - * Helper wrapping class to handle everything related to Channels; in particular - * calculating the Ess-Channels based on the Channels of the Battery and - * Battery-Inverter. Takes care of registering and unregistering listeners. - */ -public class ChannelManager extends AbstractChannelListenerManager { +public class ChannelManager extends AbstractGenericEssChannelManager { - private final GenericManagedSymmetricEss parent; - private final AllowedChargeDischargeHandler allowedChargeDischargeHandler; - - public ChannelManager(GenericManagedSymmetricEss parent) { - this.parent = parent; - this.allowedChargeDischargeHandler = new AllowedChargeDischargeHandler(parent); - } - - /** - * Called on Component activate(). - * - * @param battery the {@link Battery} - * @param batteryInverter the {@link ManagedSymmetricBatteryInverter} - */ - public void activate(ClockProvider clockProvider, Battery battery, - ManagedSymmetricBatteryInverter batteryInverter) { - /* - * Battery - */ - this.addOnSetNextValueListener(battery, Battery.ChannelId.DISCHARGE_MIN_VOLTAGE, - (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); - this.addOnSetNextValueListener(battery, Battery.ChannelId.DISCHARGE_MAX_CURRENT, - (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); - this.addOnSetNextValueListener(battery, Battery.ChannelId.CHARGE_MAX_VOLTAGE, - (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); - this.addOnSetNextValueListener(battery, Battery.ChannelId.CHARGE_MAX_CURRENT, - (ignored) -> this.allowedChargeDischargeHandler.accept(clockProvider, battery)); - this.addCopyListener(battery, // - Battery.ChannelId.CAPACITY, // - SymmetricEss.ChannelId.CAPACITY); - this.addCopyListener(battery, // - Battery.ChannelId.SOC, // - SymmetricEss.ChannelId.SOC); - - /* - * Battery-Inverter - */ - this.addCopyListener(batteryInverter, // - SymmetricBatteryInverter.ChannelId.ACTIVE_CHARGE_ENERGY, // - SymmetricEss.ChannelId.ACTIVE_CHARGE_ENERGY); - this.addCopyListener(batteryInverter, // - SymmetricBatteryInverter.ChannelId.ACTIVE_DISCHARGE_ENERGY, // - SymmetricEss.ChannelId.ACTIVE_DISCHARGE_ENERGY); - this.addCopyListener(batteryInverter, // - SymmetricBatteryInverter.ChannelId.ACTIVE_POWER, // - SymmetricEss.ChannelId.ACTIVE_POWER); - this.addCopyListener(batteryInverter, // - SymmetricBatteryInverter.ChannelId.GRID_MODE, // - SymmetricEss.ChannelId.GRID_MODE); - this.addCopyListener(batteryInverter, // - SymmetricBatteryInverter.ChannelId.MAX_APPARENT_POWER, // - SymmetricEss.ChannelId.MAX_APPARENT_POWER); - this.addCopyListener(batteryInverter, // - SymmetricBatteryInverter.ChannelId.REACTIVE_POWER, // - SymmetricEss.ChannelId.REACTIVE_POWER); - - } - - /** - * Adds a Copy-Listener. It listens on setNextValue() and copies the value to - * the target channel. - * - * @param the Channel-Type - * @param sourceComponent the source component - Battery or BatteryInverter - * @param sourceChannelId the source ChannelId - * @param targetChannelId the target ChannelId - */ - private void addCopyListener(OpenemsComponent sourceComponent, ChannelId sourceChannelId, - ChannelId targetChannelId) { - this.addOnSetNextValueListener(sourceComponent, sourceChannelId, (value) -> { - Channel targetChannel = this.parent.channel(targetChannelId); - targetChannel.setNextValue(value); - }); + public ChannelManager(GenericManagedEss parent) { + super(parent); } } diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/Config.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/Config.java index 9f4906de3ec..9dbf766a316 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/Config.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/Config.java @@ -8,7 +8,7 @@ @ObjectClassDefinition(// name = "ESS Generic Managed Symmetric", // description = "") -public @interface Config { +@interface Config { @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "ess0"; diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssImpl.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssImpl.java index 31d5fecd212..f8abdbaaff5 100644 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssImpl.java +++ b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssImpl.java @@ -1,9 +1,5 @@ package io.openems.edge.ess.generic.symmetric; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; @@ -18,29 +14,19 @@ import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import io.openems.common.channel.AccessMode; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.battery.api.Battery; -import io.openems.edge.batteryinverter.api.BatteryInverterConstraint; import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; -import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter; -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.modbusslave.ModbusSlave; -import io.openems.edge.common.modbusslave.ModbusSlaveTable; -import io.openems.edge.common.startstop.StartStop; import io.openems.edge.common.startstop.StartStoppable; import io.openems.edge.ess.api.ManagedSymmetricEss; import io.openems.edge.ess.api.SymmetricEss; -import io.openems.edge.ess.generic.symmetric.statemachine.Context; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; -import io.openems.edge.ess.power.api.Constraint; +import io.openems.edge.ess.generic.common.AbstractGenericEssChannelManager; +import io.openems.edge.ess.generic.common.AbstractGenericManagedEss; +import io.openems.edge.ess.generic.common.GenericManagedEss; import io.openems.edge.ess.power.api.Power; @Designate(ocd = Config.class, factory = true) @@ -52,8 +38,9 @@ EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // } // ) -public class GenericManagedSymmetricEssImpl extends AbstractOpenemsComponent implements GenericManagedSymmetricEss, - ManagedSymmetricEss, SymmetricEss, OpenemsComponent, EventHandler, StartStoppable, ModbusSlave { +public class GenericManagedSymmetricEssImpl extends AbstractGenericManagedEss + implements GenericManagedEss, ManagedSymmetricEss, SymmetricEss, OpenemsComponent, EventHandler, StartStoppable, + ModbusSlave { @Reference private Power power; @@ -70,19 +57,7 @@ public class GenericManagedSymmetricEssImpl extends AbstractOpenemsComponent imp @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) private Battery battery; - private final Logger log = LoggerFactory.getLogger(GenericManagedSymmetricEssImpl.class); - - /** - * Manages the {@link State}s of the StateMachine. - */ - private final StateMachine stateMachine = new StateMachine(State.UNDEFINED); - - /** - * Helper wrapping class to handle everything related to Channels. - */ - private final ChannelManager channelHandler = new ChannelManager(this); - - private Config config = null; + private final ChannelManager channelManager = new ChannelManager(this); public GenericManagedSymmetricEssImpl() { super(// @@ -90,82 +65,24 @@ public GenericManagedSymmetricEssImpl() { StartStoppable.ChannelId.values(), // SymmetricEss.ChannelId.values(), // ManagedSymmetricEss.ChannelId.values(), // - GenericManagedSymmetricEss.ChannelId.values() // + GenericManagedEss.ChannelId.values() // ); } @Activate void activate(ComponentContext context, Config config) { - super.activate(context, config.id(), config.alias(), config.enabled()); - - // update filter for 'BatteryInverter' - if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "batteryInverter", - config.batteryInverter_id())) { - return; - } - - // update filter for 'Battery' - if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "battery", config.battery_id())) { - return; - } - - this.channelHandler.activate(this.componentManager, this.battery, this.batteryInverter); - this.config = config; + super.activate(context, config.id(), config.alias(), config.enabled(), config.startStop(), this.cm, + config.batteryInverter_id(), config.battery_id()); } @Deactivate protected void deactivate() { - this.channelHandler.deactivate(); super.deactivate(); } @Override public void handleEvent(Event event) { - if (!this.isEnabled()) { - return; - } - switch (event.getTopic()) { - - case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: - this.handleStateMachine(); - break; - } - } - - /** - * Handles the State-Machine. - */ - private void handleStateMachine() { - // Store the current State - this.channel(GenericManagedSymmetricEss.ChannelId.STATE_MACHINE) - .setNextValue(this.stateMachine.getCurrentState()); - - // Initialize 'Start-Stop' Channel - this._setStartStop(StartStop.UNDEFINED); - - // Prepare Context - Context context = new Context(this, this.battery, this.batteryInverter, this.config); - - // Call the StateMachine - try { - this.stateMachine.run(context); - - this.channel(GenericManagedSymmetricEss.ChannelId.RUN_FAILED).setNextValue(false); - - } catch (OpenemsNamedException e) { - this.channel(GenericManagedSymmetricEss.ChannelId.RUN_FAILED).setNextValue(true); - this.logError(this.log, "StateMachine failed: " + e.getMessage()); - } - } - - @Override - public String debugLog() { - return "SoC:" + this.getSoc().asString() // - + "|L:" + this.getActivePower().asString() // - + "|Allowed:" // - + this.getAllowedChargePower().asStringWithoutUnit() + ";" // - + this.getAllowedDischargePower().asString() // - + "|" + this.channel(GenericManagedSymmetricEss.ChannelId.STATE_MACHINE).value().asOptionString(); + super.handleEvent(event); } @Override @@ -173,83 +90,24 @@ public Power getPower() { return this.power; } - /** - * Forwards the power request to the {@link SymmetricBatteryInverter}. - * - * {@inheritDoc} - */ @Override - public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException { - this.batteryInverter.run(this.battery, activePower, reactivePower); + protected AbstractGenericEssChannelManager getChannelManager() { + return this.channelManager; } - /** - * Retrieves PowerPrecision from {@link SymmetricBatteryInverter}. - * - * {@inheritDoc} - */ @Override - public int getPowerPrecision() { - return this.batteryInverter.getPowerPrecision(); - } - - /** - * Retrieves StaticConstraints from {@link SymmetricBatteryInverter}. - * - * {@inheritDoc} - */ - @Override - public Constraint[] getStaticConstraints() throws OpenemsNamedException { - - List result = new ArrayList(); - - // Get BatteryInverterConstraints - BatteryInverterConstraint[] constraints = this.batteryInverter.getStaticConstraints(); - - for (int i = 0; i < constraints.length; i++) { - BatteryInverterConstraint c = constraints[i]; - result.add(this.power.createSimpleConstraint(c.description, this, c.phase, c.pwr, c.relationship, c.value)); - } - - return result.toArray(new Constraint[result.size()]); + protected Battery getBattery() { + return this.battery; } - private AtomicReference startStopTarget = new AtomicReference(StartStop.UNDEFINED); - @Override - public void setStartStop(StartStop value) { - if (this.startStopTarget.getAndSet(value) != value) { - // Set only if value changed - this.stateMachine.forceNextState(State.UNDEFINED); - } + protected ManagedSymmetricBatteryInverter getBatteryInverter() { + return this.batteryInverter; } @Override - public StartStop getStartStopTarget() { - switch (this.config.startStop()) { - case AUTO: - // read StartStop-Channel - return this.startStopTarget.get(); - - case START: - // force START - return StartStop.START; - - case STOP: - // force STOP - return StartStop.STOP; - } - - assert false; - return StartStop.UNDEFINED; // can never happen + protected ComponentManager getComponentManager() { + return this.componentManager; } - @Override - public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { - return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(accessMode), // - SymmetricEss.getModbusSlaveNatureTable(accessMode), // - ManagedSymmetricEss.getModbusSlaveNatureTable(accessMode) // - ); - } } diff --git a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/Context.java b/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/Context.java deleted file mode 100644 index b27c6f3a2dc..00000000000 --- a/io.openems.edge.ess.generic/src/io/openems/edge/ess/generic/symmetric/statemachine/Context.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.openems.edge.ess.generic.symmetric.statemachine; - -import io.openems.edge.battery.api.Battery; -import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; -import io.openems.edge.common.statemachine.AbstractContext; -import io.openems.edge.ess.generic.symmetric.Config; -import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEss; - -public class Context extends AbstractContext { - - protected final Battery battery; - protected final ManagedSymmetricBatteryInverter batteryInverter; - protected final Config config; - - public Context(GenericManagedSymmetricEss parent, Battery battery, ManagedSymmetricBatteryInverter batteryInverter, - Config config) { - super(parent); - this.battery = battery; - this.batteryInverter = batteryInverter; - this.config = config; - } -} \ No newline at end of file diff --git a/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/AllowedChargeDischargeHandlerTest.java b/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/common/AllowedChargeDischargeHandlerTest.java similarity index 96% rename from io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/AllowedChargeDischargeHandlerTest.java rename to io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/common/AllowedChargeDischargeHandlerTest.java index db3b760264d..92cd8a87270 100644 --- a/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/AllowedChargeDischargeHandlerTest.java +++ b/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/common/AllowedChargeDischargeHandlerTest.java @@ -1,4 +1,4 @@ -package io.openems.edge.ess.generic.symmetric; +package io.openems.edge.ess.generic.common; import static org.junit.Assert.assertEquals; @@ -12,6 +12,7 @@ import io.openems.edge.common.test.ComponentTest; import io.openems.edge.common.test.DummyComponentManager; import io.openems.edge.common.test.TimeLeapClock; +import io.openems.edge.ess.generic.symmetric.GenericManagedSymmetricEssImpl; public class AllowedChargeDischargeHandlerTest { diff --git a/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssTest.java b/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssTest.java index 263a0e5a969..34397dd597f 100644 --- a/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssTest.java +++ b/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/GenericManagedSymmetricEssTest.java @@ -15,7 +15,8 @@ import io.openems.edge.common.test.DummyComponentManager; import io.openems.edge.common.test.DummyConfigurationAdmin; import io.openems.edge.common.test.TimeLeapClock; -import io.openems.edge.ess.generic.symmetric.statemachine.StateMachine.State; +import io.openems.edge.ess.generic.common.GenericManagedEss; +import io.openems.edge.ess.generic.common.statemachine.StateMachine.State; import io.openems.edge.ess.test.DummyPower; import io.openems.edge.ess.test.ManagedSymmetricEssTest; @@ -97,8 +98,7 @@ public void testForceCharge() throws Exception { .next(new TestCase() // .input(BATTERY_CHARGE_MAX_CURRENT, 50) // .input(BATTERY_DISCHARGE_MAX_CURRENT, -5) // - .output(ESS_ALLOWED_DISCHARGE_POWER, - (int) (-2500 * GenericManagedSymmetricEss.EFFICIENCY_FACTOR))) // + .output(ESS_ALLOWED_DISCHARGE_POWER, (int) (-2500 * GenericManagedEss.EFFICIENCY_FACTOR))) // ; } diff --git a/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/MyConfig.java b/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/MyConfig.java index 393fc5deb31..5ad9cbf5a85 100644 --- a/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/MyConfig.java +++ b/io.openems.edge.ess.generic/test/io/openems/edge/ess/generic/symmetric/MyConfig.java @@ -3,6 +3,7 @@ import io.openems.common.utils.ConfigUtils; import io.openems.edge.common.startstop.StartStopConfig; import io.openems.edge.common.test.AbstractComponentConfig; +import io.openems.edge.ess.generic.symmetric.Config; @SuppressWarnings("all") public class MyConfig extends AbstractComponentConfig implements Config { diff --git a/io.openems.edge.ess.mr.gridcon/test/io/openems/edge/ess/mr/gridcon/helper/DummyBattery.java b/io.openems.edge.ess.mr.gridcon/test/io/openems/edge/ess/mr/gridcon/helper/DummyBattery.java index 17c08393815..2cf23c4e131 100644 --- a/io.openems.edge.ess.mr.gridcon/test/io/openems/edge/ess/mr/gridcon/helper/DummyBattery.java +++ b/io.openems.edge.ess.mr.gridcon/test/io/openems/edge/ess/mr/gridcon/helper/DummyBattery.java @@ -41,8 +41,6 @@ public DummyBattery(// setMaximalDischargeCurrent(DEFAULT_MAX_DISCHARGE_CURRENT); setDischargeMinVoltage(DEFAULT_MIN_VOLTAGE); setChargeMaxVoltage(DEFAULT_MAX_VOLTAGE); - setForceChargeActive(false); - setForceDischargeActive(false); } public void setMinimalCellVoltage(int minimalCellVoltage) { @@ -140,16 +138,6 @@ public void setCurrent(int current) { this.getCurrentChannel().nextProcessImage(); } - public void setForceChargeActive(boolean value) { - this._setForceChargeActive(value); - this.getForceChargeActiveChannel().nextProcessImage(); - } - - public void setForceDischargeActive(boolean value) { - this._setForceDischargeActive(value); - this.getForceDischargeActiveChannel().nextProcessImage(); - } - @Override public void start() throws OpenemsNamedException { this.setStartStop(StartStop.START); diff --git a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/ChannelManager.java b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/ChannelManager.java index 3eca32dab8c..a02646efa87 100644 --- a/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/ChannelManager.java +++ b/io.openems.edge.ess.sinexcel/src/io/openems/edge/ess/sinexcel/ChannelManager.java @@ -3,7 +3,7 @@ import io.openems.edge.battery.api.Battery; import io.openems.edge.common.channel.AbstractChannelListenerManager; import io.openems.edge.common.component.ClockProvider; -import io.openems.edge.ess.generic.symmetric.AllowedChargeDischargeHandler; +import io.openems.edge.ess.generic.common.AllowedChargeDischargeHandler; public class ChannelManager extends AbstractChannelListenerManager { diff --git a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java index bb13f768467..cceac73e5cc 100644 --- a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java +++ b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/Evcs.java @@ -2,6 +2,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; @@ -31,7 +32,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ STATUS(Doc.of(Status.values()) // - .accessMode(AccessMode.READ_ONLY)), + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Charge Power. @@ -44,8 +46,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CHARGE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT) // - .accessMode(AccessMode.READ_ONLY)), // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Charging Type. @@ -60,7 +62,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CHARGING_TYPE(Doc.of(ChargingType.values()) // - .accessMode(AccessMode.READ_ONLY)), // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Count of phases, the EV is charging with. @@ -76,7 +79,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ PHASES(Doc.of(OpenemsType.INTEGER) // - .accessMode(AccessMode.READ_ONLY)), // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Minimum Power valid by the hardware. @@ -94,7 +98,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MINIMUM_HARDWARE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // - .accessMode(AccessMode.READ_ONLY)), + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Maximum Power valid by the hardware. @@ -108,7 +113,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MAXIMUM_HARDWARE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // - .accessMode(AccessMode.READ_ONLY)), + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Maximum Power defined by software. @@ -122,7 +128,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MAXIMUM_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // - .accessMode(AccessMode.READ_ONLY)), + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Minimum Power defined by software. @@ -136,7 +143,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ MINIMUM_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // - .accessMode(AccessMode.READ_ONLY)), + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Energy that was charged during the current or last Session. @@ -150,7 +158,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ENERGY_SESSION(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT_HOURS) // - .accessMode(AccessMode.READ_ONLY)), + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Active Consumption Energy. @@ -163,8 +172,9 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_CONSUMPTION_ENERGY(Doc.of(OpenemsType.LONG) // .unit(Unit.WATT_HOURS) // - .accessMode(AccessMode.READ_ONLY)), - + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // + /** * Failed state channel for a failed communication to the EVCS. * @@ -174,7 +184,9 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Level: FAULT * */ - CHARGINGSTATION_COMMUNICATION_FAILED(Doc.of(Level.FAULT).accessMode(AccessMode.READ_ONLY)); + CHARGINGSTATION_COMMUNICATION_FAILED(Doc.of(Level.FAULT) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)); // private final Doc doc; @@ -512,7 +524,7 @@ public default void _setEnergySession(Integer value) { public default void _setEnergySession(int value) { this.getEnergySessionChannel().setNextValue(value); } - + /** * Gets the Channel for {@link ChannelId#ACTIVE_CONSUMPTION_ENERGY}. * diff --git a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/ManagedEvcs.java b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/ManagedEvcs.java index bcee9d06b31..4c5746a2068 100644 --- a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/ManagedEvcs.java +++ b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/ManagedEvcs.java @@ -3,6 +3,7 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.OpenemsType; @@ -25,7 +26,7 @@ public interface ManagedEvcs extends Evcs { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { /** - * Gets the smallest power steps that can be set (given in W). + * Gets the smallest power steps that can be set (given in W). * *

    * Example: @@ -45,8 +46,9 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ POWER_PRECISION(Doc.of(OpenemsType.DOUBLE) // .unit(Unit.WATT) // - .accessMode(AccessMode.READ_ONLY)), - + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // + /** * Sets the charge power limit of the EVCS in [W]. * @@ -77,7 +79,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ SET_CHARGE_POWER_LIMIT(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // - .accessMode(AccessMode.READ_WRITE)), + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Applies the configured filter in {@link EvcsPowerComponent} and sets a the @@ -121,7 +124,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ IS_CLUSTERED(Doc.of(OpenemsType.BOOLEAN) // - .accessMode(AccessMode.READ_ONLY)), // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Sets a Text that is shown on the display of the EVCS. @@ -200,8 +204,8 @@ public default Value getPowerPrecision() { } /** - * Internal method to set the 'nextValue' on - * {@link ChannelId#POWER_PRECISION} Channel. + * Internal method to set the 'nextValue' on {@link ChannelId#POWER_PRECISION} + * Channel. * * @param value the next value */ @@ -210,15 +214,15 @@ public default void _setPowerPrecision(Double value) { } /** - * Internal method to set the 'nextValue' on - * {@link ChannelId#POWER_PRECISION} Channel. + * Internal method to set the 'nextValue' on {@link ChannelId#POWER_PRECISION} + * Channel. * * @param value the next value */ public default void _setPowerPrecision(double value) { this.getPowerPrecisionChannel().setNextValue(value); } - + /** * Gets the Channel for {@link ChannelId#SET_CHARGE_POWER_LIMIT}. * diff --git a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/MeasuringEvcs.java b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/MeasuringEvcs.java index 21886aceda6..4715ed0eb5a 100644 --- a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/MeasuringEvcs.java +++ b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/MeasuringEvcs.java @@ -1,6 +1,7 @@ package io.openems.edge.evcs.api; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; @@ -22,7 +23,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *

  • Unit: mA * */ - CURRENT_TO_GRID(Doc.of(OpenemsType.INTEGER).unit(Unit.MILLIAMPERE).accessMode(AccessMode.READ_ONLY) + CURRENT_TO_GRID(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.MILLIAMPERE) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Instantaneous current flow from EV")), /** @@ -38,7 +42,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: mA * */ - CURRENT_TO_EV(Doc.of(OpenemsType.INTEGER).unit(Unit.MILLIAMPERE).accessMode(AccessMode.READ_ONLY) + CURRENT_TO_EV(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.MILLIAMPERE) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Instantaneous current flow to EV")), /** @@ -54,7 +61,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: mA * */ - CURRENT_OFFERED(Doc.of(OpenemsType.INTEGER).unit(Unit.MILLIAMPERE).accessMode(AccessMode.READ_ONLY) + CURRENT_OFFERED(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.MILLIAMPERE) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Current offered")), /** @@ -72,7 +82,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Wh * */ - ENERGY_ACTIVE_TO_GRID(Doc.of(OpenemsType.DOUBLE).unit(Unit.WATT_HOURS).accessMode(AccessMode.READ_ONLY) + ENERGY_ACTIVE_TO_GRID(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.WATT_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Active energy to grid")), /** @@ -90,7 +103,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Wh * */ - ENERGY_ACTIVE_TO_EV(Doc.of(OpenemsType.DOUBLE).unit(Unit.WATT_HOURS).accessMode(AccessMode.READ_ONLY) + ENERGY_ACTIVE_TO_EV(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.WATT_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Active energy to ev")), /** @@ -108,8 +124,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: VARh * */ - ENERGY_REACTIVE_TO_GRID(Doc.of(OpenemsType.DOUBLE).unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) - .accessMode(AccessMode.READ_ONLY).text("Energy.Reactive.Export.Register")), + ENERGY_REACTIVE_TO_GRID(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Energy.Reactive.Export.Register")), /** * Reactive energy to EV (import). @@ -126,8 +145,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: VARh * */ - ENERGY_REACTIVE_TO_EV(Doc.of(OpenemsType.DOUBLE).unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) - .accessMode(AccessMode.READ_ONLY).text("Energy.Reactive.Import.Register")), + ENERGY_REACTIVE_TO_EV(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Energy.Reactive.Import.Register")), /** * Active energy to grid (export) in an interval. @@ -145,7 +167,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Wh * */ - ENERGY_ACTIVE_TO_GRID_INTERVAL(Doc.of(OpenemsType.DOUBLE).unit(Unit.WATT_HOURS).accessMode(AccessMode.READ_ONLY) + ENERGY_ACTIVE_TO_GRID_INTERVAL(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.WATT_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Energy.Active.Export.Interval")), /** @@ -164,7 +189,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Wh * */ - ENERGY_ACTIVE_TO_EV_INTERVAL(Doc.of(OpenemsType.DOUBLE).unit(Unit.WATT_HOURS).accessMode(AccessMode.READ_ONLY) + ENERGY_ACTIVE_TO_EV_INTERVAL(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.WATT_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Energy.Active.Import.Interval")), /** @@ -183,8 +211,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: VARh * */ - ENERGY_REACTIVE_TO_GRID_INTERVAL(Doc.of(OpenemsType.DOUBLE).unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) - .accessMode(AccessMode.READ_ONLY).text("Energy.Reactive.Export.Interval")), + ENERGY_REACTIVE_TO_GRID_INTERVAL(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Energy.Reactive.Export.Interval")), /** * Reactive energy to EV (import) in an interval. @@ -203,8 +234,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: VARh * */ - ENERGY_REACTIVE_TO_EV_INTERVAL(Doc.of(OpenemsType.DOUBLE).unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) - .accessMode(AccessMode.READ_ONLY).text("Energy.Reactive.Import.Interval")), + ENERGY_REACTIVE_TO_EV_INTERVAL(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.VOLT_AMPERE_REACTIVE_HOURS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Energy.Reactive.Import.Interval")), /** * Frequency. @@ -221,7 +255,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: Hz * */ - FREQUENCY(Doc.of(OpenemsType.STRING).unit(Unit.HERTZ).accessMode(AccessMode.READ_ONLY).text("Frequency")), + FREQUENCY(Doc.of(OpenemsType.STRING) // + .unit(Unit.HERTZ) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Frequency")), /** * Active power to grid (export) @@ -236,7 +274,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: W * */ - POWER_ACTIVE_TO_GRID(Doc.of(OpenemsType.INTEGER).unit(Unit.WATT).accessMode(AccessMode.READ_ONLY) + POWER_ACTIVE_TO_GRID(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // .text("Power.Active.Export")), /** @@ -251,7 +292,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Type: String * */ - POWER_FACTOR(Doc.of(OpenemsType.STRING).accessMode(AccessMode.READ_ONLY).text("Power.Factor")), + POWER_FACTOR(Doc.of(OpenemsType.STRING) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Power.Factor")), /** * Power offered. @@ -266,8 +310,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: W * */ - POWER_OFFERED( - Doc.of(OpenemsType.INTEGER).unit(Unit.WATT).accessMode(AccessMode.READ_ONLY).text("Power.Offered")), + POWER_OFFERED(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Power.Offered")), /** * Reactive power to grid (export). @@ -282,8 +329,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: VAR * */ - POWER_REACTIVE_TO_GRID(Doc.of(OpenemsType.INTEGER).unit(Unit.VOLT_AMPERE_REACTIVE) - .accessMode(AccessMode.READ_ONLY).text("Power.Reactive.Export")), + POWER_REACTIVE_TO_GRID(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT_AMPERE_REACTIVE) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Power.Reactive.Export")), /** * Reactive power to EV (import). @@ -298,8 +348,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: VAR * */ - POWER_REACTIVE_TO_EV(Doc.of(OpenemsType.INTEGER).unit(Unit.VOLT_AMPERE_REACTIVE) - .accessMode(AccessMode.READ_ONLY).text("Power.Reactive.Import")), + POWER_REACTIVE_TO_EV(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT_AMPERE_REACTIVE) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Power.Reactive.Import")), /** * Fan speed. @@ -313,7 +366,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Type: String * */ - RPM(Doc.of(OpenemsType.STRING).accessMode(AccessMode.READ_ONLY).text("Fan speed")), + RPM(Doc.of(OpenemsType.STRING) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Fan speed")), /** * Voltage. @@ -327,7 +383,10 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Type: String * */ - VOLTAGE(Doc.of(OpenemsType.STRING).accessMode(AccessMode.READ_ONLY).text("Voltage")), + VOLTAGE(Doc.of(OpenemsType.STRING) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Voltage")), /** * Temperature. @@ -342,8 +401,11 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { *
  • Unit: C * */ - TEMPERATURE(Doc.of(OpenemsType.STRING).unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY) - .text("Temperature")),; + TEMPERATURE(Doc.of(OpenemsType.STRING) // + .unit(Unit.DEGREE_CELSIUS) // + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH) // + .text("Temperature")); private final Doc doc; diff --git a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/SocEvcs.java b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/SocEvcs.java index 82cb1c74edc..267506071b8 100644 --- a/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/SocEvcs.java +++ b/io.openems.edge.evcs.api/src/io/openems/edge/evcs/api/SocEvcs.java @@ -1,6 +1,7 @@ package io.openems.edge.evcs.api; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; @@ -28,7 +29,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ SOC(Doc.of(OpenemsType.INTEGER) // .unit(Unit.PERCENT) // - .accessMode(AccessMode.READ_ONLY)); + .accessMode(AccessMode.READ_ONLY) // + .persistencePriority(PersistencePriority.HIGH)); // // TODO: If there are EVCSs with more information maybe a Channel // TIME_TILL_CHARGING_FINISHED is possible diff --git a/io.openems.edge.goodwe/bnd.bnd b/io.openems.edge.goodwe/bnd.bnd index 6410f917d14..5b095168930 100644 --- a/io.openems.edge.goodwe/bnd.bnd +++ b/io.openems.edge.goodwe/bnd.bnd @@ -6,6 +6,8 @@ Bundle-Version: 1.0.0.${tstamp} -buildpath: \ ${buildpath},\ io.openems.common,\ + io.openems.edge.battery.api,\ + io.openems.edge.batteryinverter.api,\ io.openems.edge.bridge.modbus,\ io.openems.edge.common,\ io.openems.edge.ess.api,\ diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/Config.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/Config.java new file mode 100644 index 00000000000..69b5df989f9 --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/Config.java @@ -0,0 +1,32 @@ +package io.openems.edge.goodwe.batteryinverter; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.edge.goodwe.GoodWeConstants; + +@ObjectClassDefinition(// + name = "GoodWe Battery-Inverter", // + description = "GoodWe Battery Inverter.") + +@interface Config { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "batteryInverter0"; + + @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 = "Modbus-ID", description = "ID of Modbus bridge.") + String modbus_id() default "modbus0"; + + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") + int modbusUnitId() default GoodWeConstants.DEFAULT_UNIT_ID; + + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") + String Modbus_target() default ""; + + String webconsole_configurationFactory_nameHint() default "GoodWe Battery Inverter [{id}]"; +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverter.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverter.java new file mode 100644 index 00000000000..1f38b0465e0 --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverter.java @@ -0,0 +1,30 @@ +package io.openems.edge.goodwe.batteryinverter; + +import io.openems.common.channel.Level; +import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; +import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.goodwe.common.GoodWe; + +public interface GoodWeBatteryInverter + extends GoodWe, ManagedSymmetricBatteryInverter, SymmetricBatteryInverter, OpenemsComponent { + + public static enum ChannelId implements io.openems.edge.common.channel.ChannelId { + RUN_FAILED(Doc.of(Level.FAULT) // + .text("Running the Logic failed")), // + ; + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + @Override + public Doc doc() { + return this.doc; + } + } + +} 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 new file mode 100644 index 00000000000..49c879b6e2b --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java @@ -0,0 +1,239 @@ +package io.openems.edge.goodwe.batteryinverter; + +import java.util.Objects; +import java.util.Optional; + +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.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.metatype.annotations.Designate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.edge.battery.api.Battery; +import io.openems.edge.batteryinverter.api.HybridManagedSymmetricBatteryInverter; +import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; +import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.common.channel.EnumWriteChannel; +import io.openems.edge.common.channel.IntegerWriteChannel; +import io.openems.edge.common.channel.value.Value; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.startstop.StartStop; +import io.openems.edge.common.startstop.StartStoppable; +import io.openems.edge.ess.power.api.Power; +import io.openems.edge.goodwe.common.AbstractGoodWe; +import io.openems.edge.goodwe.common.GoodWe; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine; +import io.openems.edge.goodwe.common.applypower.Context; +import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.timedata.api.TimedataProvider; +import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "GoodWe.BatteryInverter", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) // +public class GoodWeBatteryInverterImpl extends AbstractGoodWe + implements GoodWeBatteryInverter, GoodWe, HybridManagedSymmetricBatteryInverter, + ManagedSymmetricBatteryInverter, SymmetricBatteryInverter, OpenemsComponent, TimedataProvider { + + private final Logger log = LoggerFactory.getLogger(GoodWeBatteryInverterImpl.class); + + @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) + private volatile Timedata timedata = null; + + @Reference + protected ConfigurationAdmin cm; + + @Reference + private Power power; + + private final CalculateEnergyFromPower calculateAcChargeEnergy = new CalculateEnergyFromPower(this, + SymmetricBatteryInverter.ChannelId.ACTIVE_CHARGE_ENERGY); + private final CalculateEnergyFromPower calculateAcDischargeEnergy = new CalculateEnergyFromPower(this, + SymmetricBatteryInverter.ChannelId.ACTIVE_DISCHARGE_ENERGY); + + private final ApplyPowerStateMachine applyPowerStateMachine = new ApplyPowerStateMachine( + ApplyPowerStateMachine.State.UNDEFINED); + + // For Fenecon Home Battery, Lead Battery Capacity has to be set as a battery + // parameter + // TODO get from Battery + private static final int LEAD_BATTERY_CAPACITY = 200; + // Fenecon Home Battery Static module min voltage, used to calculate battery + // module number per tower + // TODO get from Battery + private static final int MODULE_MIN_VOLTAGE = 42; + + /** + * Holds the latest known SoC. Updated in {@link #run(Battery, int, int)}. + */ + private Value lastSoc = null; + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + @Activate + void activate(ComponentContext context, Config config) throws OpenemsNamedException { + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id())) { + return; + } + } + + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + public GoodWeBatteryInverterImpl() throws OpenemsNamedException { + super(// + OpenemsComponent.ChannelId.values(), // + StartStoppable.ChannelId.values(), // + SymmetricBatteryInverter.ChannelId.values(), // + ManagedSymmetricBatteryInverter.ChannelId.values(), // + HybridManagedSymmetricBatteryInverter.ChannelId.values(), // + GoodWe.ChannelId.values(), // + GoodWeBatteryInverter.ChannelId.values() // + ); + // GoodWe is always started + this._setStartStop(StartStop.START); + } + + private void updatechannels() { + /* + * Update ActivePower from P_BATTERY1 and chargers ACTUAL_POWER + */ + Integer activePower = this.calculateActivePower(); + this._setActivePower(activePower); + + /* + * Calculate AC Energy + */ + if (activePower == null) { + // Not available + this.calculateAcChargeEnergy.update(null); + this.calculateAcDischargeEnergy.update(null); + } else if (activePower > 0) { + // Discharge + this.calculateAcChargeEnergy.update(0); + this.calculateAcDischargeEnergy.update(activePower); + } else { + // Charge + this.calculateAcChargeEnergy.update(activePower * -1); + this.calculateAcDischargeEnergy.update(0); + } + } + + /** + * Sets the Battery Limits. + * + * @param battery the linked {@link Battery} + * @throws OpenemsNamedException on error + */ + private void setBatteryLimits(Battery battery) throws OpenemsNamedException { + // Battery String + IntegerWriteChannel bmsBatteryString = this.channel(GoodWe.ChannelId.BATT_STRINGS); + if (!Objects.equals(bmsBatteryString.value().orElse(0), + (battery.getDischargeMinVoltage().orElse(0) / MODULE_MIN_VOLTAGE))) { + bmsBatteryString.setNextWriteValue(battery.getDischargeMinVoltage().orElse(0) / MODULE_MIN_VOLTAGE); + } + + IntegerWriteChannel bmsLeadBatCapacity = this.channel(GoodWe.ChannelId.LEAD_BAT_CAPACITY); + if (!Objects.equals(bmsLeadBatCapacity.value().orElse(0), LEAD_BATTERY_CAPACITY)) { + bmsLeadBatCapacity.setNextWriteValue(LEAD_BATTERY_CAPACITY); + } + + IntegerWriteChannel bmsVoltUnderMin = this.channel(GoodWe.ChannelId.BATT_VOLT_UNDER_MIN); + if (!Objects.equals(bmsVoltUnderMin.value().get(), battery.getDischargeMinVoltage().get())) { + bmsVoltUnderMin.setNextWriteValueFromObject(battery.getDischargeMinVoltage()); + } + } + + @Override + public int getPowerPrecision() { + return 1; + } + + @Override + public Timedata getTimedata() { + return this.timedata; + } + + @Override + public Integer getSurplusPower() { + if (this.lastSoc.orElse(0) < 99) { + return null; + } + Integer productionPower = this.calculatePvProduction(); + if (productionPower == null || productionPower < 100) { + return null; + } + return productionPower; + } + + @Override + public void setStartStop(StartStop value) throws OpenemsNamedException { + // GoodWe is always started. This has no effect. + } + + @Override + public void run(Battery battery, int setActivePower, int setReactivePower) throws OpenemsNamedException { + + // Set Battery Limits + this.setBatteryLimits(battery); + + // Calculate ActivePower and Energy values. + this.updatechannels(); + this.lastSoc = battery.getSoc(); + + // Update ApplyPowerStateMachine + int pvProduction = Optional.ofNullable(this.calculatePvProduction()).orElse(0); + int soc = battery.getSoc().orElse(0); + ApplyPowerStateMachine.State state = ApplyPowerStateMachine.evaluateState(this.getGoodweType(), + false /* read-only mode is never true */, pvProduction, soc, setActivePower); + + // Store the current State + this.channel(GoodWe.ChannelId.APPLY_POWER_STATE_MACHINE).setNextValue(state); + + // Prepare Context + Context context = new Context(this, Optional.ofNullable(pvProduction).orElse(0), setActivePower); + + // Call the StateMachine + try { + + this.applyPowerStateMachine.forceNextState(state); + this.applyPowerStateMachine.run(context); // apply the force next state + this.applyPowerStateMachine.run(context); // execute correct handler + IntegerWriteChannel emsPowerSetChannel = this.channel(GoodWe.ChannelId.EMS_POWER_SET); + emsPowerSetChannel.setNextWriteValue(context.getEssPowerSet()); + + EnumWriteChannel emsPowerModeChannel = this.channel(GoodWe.ChannelId.EMS_POWER_MODE); + emsPowerModeChannel.setNextWriteValue(context.getNextPowerMode()); + + this.channel(GoodWeBatteryInverter.ChannelId.RUN_FAILED).setNextValue(false); + + } catch (OpenemsNamedException e) { + this.channel(GoodWeBatteryInverter.ChannelId.RUN_FAILED).setNextValue(true); + this.logError(this.log, "StateMachine failed: " + e.getMessage()); + } + } + + @Override + public String debugLog() { + return this.applyPowerStateMachine.getCurrentState().asCamelCase(); + } +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV1.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV1.java index 901c92bdcc0..d315d8f7a8c 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV1.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV1.java @@ -6,10 +6,10 @@ import io.openems.edge.goodwe.GoodWeConstants; @ObjectClassDefinition(// - name = "GoodWe ET Charger PV1", // - description = "Implements the Goodwe-ET Charger.") + name = "GoodWe Charger PV1", // + description = "Implements the GoodWe-ET Charger 1.") -public @interface ConfigPV1 { +@interface ConfigPV1 { @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "charger0"; @@ -19,20 +19,20 @@ @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") boolean enabled() default true; - @AttributeDefinition(name = "GoodWe ET ESS", description = "ID of GoodWe ET Energy Storage System.") - String ess_id() default "ess0"; + @AttributeDefinition(name = "GoodWe ESS or Battery-Inverter", description = "ID of GoodWe Energy Storage System or Battery-Inverter.") + String essOrBatteryInverter_id() default "batteryInverter0"; - @AttributeDefinition(name = "GoodWe ET ESS target filter", description = "This is auto-generated by 'GoodWe ET ESS'.") - String Ess_target() default ""; - - @AttributeDefinition(name = "Modbus Unit-id", description = "Unit-id") - int unit_id() default GoodWeConstants.DEFAULT_UNIT_ID; + @AttributeDefinition(name = "GoodWe ESS or Battery-Inverter target filter", description = "This is auto-generated by 'GoodWe ESS or Battery-Inverter'.") + String essOrBatteryInverter_target() default ""; @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.") String modbus_id() default "modbus0"; + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") + int modbusUnitId() default GoodWeConstants.DEFAULT_UNIT_ID; + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "GoodWe ET Charger PV1 [{id}]"; -} \ No newline at end of file + String webconsole_configurationFactory_nameHint() default "GoodWe Charger PV1 [{id}]"; +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV2.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV2.java index 04d9e26bef8..588e88ef3b2 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV2.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/ConfigPV2.java @@ -6,10 +6,10 @@ import io.openems.edge.goodwe.GoodWeConstants; @ObjectClassDefinition(// - name = "GoodWe ET Charger PV2", // - description = "Implements the Goodwe-ET Charger.") + name = "GoodWe Charger PV2", // + description = "Implements the GoodWe-ET Charger 2.") -public @interface ConfigPV2 { +@interface ConfigPV2 { @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "charger1"; @@ -19,20 +19,20 @@ @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") boolean enabled() default true; - @AttributeDefinition(name = "GoodWe ET ESS", description = "ID of GoodWe ET Energy Storage System.") - String ess_id() default "ess0"; + @AttributeDefinition(name = "GoodWe ESS or Battery-Inverter", description = "ID of GoodWe Energy Storage System or Battery-Inverter.") + String essOrBatteryInverter_id() default "batteryInverter0"; - @AttributeDefinition(name = "GoodWe ET ESS target filter", description = "This is auto-generated by 'GoodWe ET ESS'.") - String Ess_target() default ""; - - @AttributeDefinition(name = "Modbus Unit-id", description = "Unit-id") - int unit_id() default GoodWeConstants.DEFAULT_UNIT_ID; + @AttributeDefinition(name = "GoodWe ESS or Battery-Inverter target filter", description = "This is auto-generated by 'GoodWe ESS or Battery-Inverter'.") + String essOrBatteryInverter_target() default ""; @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.") String modbus_id() default "modbus0"; + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") + int modbusUnitId() default GoodWeConstants.DEFAULT_UNIT_ID; + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "GoodWe ET Charger PV2 [{id}]"; -} \ No newline at end of file + String webconsole_configurationFactory_nameHint() default "GoodWe Charger PV2 [{id}]"; +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv1.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv1.java index ea12527cf61..f3fbbbd5ee7 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv1.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv1.java @@ -19,7 +19,7 @@ import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.ess.dccharger.api.EssDcCharger; -import io.openems.edge.goodwe.ess.GoodWeEss; +import io.openems.edge.goodwe.common.GoodWe; import io.openems.edge.timedata.api.Timedata; import io.openems.edge.timedata.api.TimedataProvider; @@ -38,7 +38,7 @@ public class GoodWeChargerPv1 extends AbstractGoodWeEtCharger protected ConfigurationAdmin cm; @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) - private GoodWeEss ess; + private GoodWe essOrBatteryInverter; @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) private volatile Timedata timedata = null; @@ -54,25 +54,26 @@ public GoodWeChargerPv1() { @Activate void activate(ComponentContext context, ConfigPV1 config) throws OpenemsException { - if (super.activate(context, config.id(), config.alias(), config.enabled(), config.unit_id(), this.cm, "Modbus", - config.modbus_id())) { + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id())) { return; } // update filter for 'Ess' - if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "ess", config.ess_id())) { + if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "essOrBatteryInverter", + config.essOrBatteryInverter_id())) { return; } - if (this.ess != null) { - this.ess.addCharger(this); + if (this.essOrBatteryInverter != null) { + this.essOrBatteryInverter.addCharger(this); } } @Deactivate protected void deactivate() { - if (this.ess != null) { - this.ess.removeCharger(this); + if (this.essOrBatteryInverter != null) { + this.essOrBatteryInverter.removeCharger(this); } super.deactivate(); } diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv2.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv2.java index 7f78a42e8ee..007cc71b4de 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv2.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/GoodWeChargerPv2.java @@ -19,7 +19,7 @@ import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.ess.dccharger.api.EssDcCharger; -import io.openems.edge.goodwe.ess.GoodWeEss; +import io.openems.edge.goodwe.common.GoodWe; import io.openems.edge.timedata.api.Timedata; import io.openems.edge.timedata.api.TimedataProvider; @@ -38,7 +38,7 @@ public class GoodWeChargerPv2 extends AbstractGoodWeEtCharger protected ConfigurationAdmin cm; @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) - private GoodWeEss ess; + private GoodWe essOrBatteryInverter; @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) private volatile Timedata timedata = null; @@ -54,25 +54,26 @@ public GoodWeChargerPv2() { @Activate void activate(ComponentContext context, ConfigPV2 config) throws OpenemsException { - if (super.activate(context, config.id(), config.alias(), config.enabled(), config.unit_id(), this.cm, "Modbus", - config.modbus_id())) { + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id())) { return; } // update filter for 'Ess' - if (OpenemsComponent.updateReferenceFilter(cm, this.servicePid(), "ess", config.ess_id())) { + if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "essOrBatteryInverter", + config.essOrBatteryInverter_id())) { return; } - if (this.ess != null) { - this.ess.addCharger(this); + if (this.essOrBatteryInverter != null) { + this.essOrBatteryInverter.addCharger(this); } } @Deactivate protected void deactivate() { - if (this.ess != null) { - this.ess.removeCharger(this); + if (this.essOrBatteryInverter != null) { + this.essOrBatteryInverter.removeCharger(this); } super.deactivate(); } diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/AbstractGoodWe.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/AbstractGoodWe.java new file mode 100644 index 00000000000..2b143019073 --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/AbstractGoodWe.java @@ -0,0 +1,446 @@ +package io.openems.edge.goodwe.common; + +import java.util.HashSet; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.NotImplementedException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.OpenemsType; +import io.openems.edge.batteryinverter.api.HybridManagedSymmetricBatteryInverter; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; +import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.AbstractModbusElement; +import io.openems.edge.bridge.modbus.api.element.BitsWordElement; +import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; +import io.openems.edge.bridge.modbus.api.element.SignedWordElement; +import io.openems.edge.bridge.modbus.api.element.StringWordElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; +import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; +import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; +import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; +import io.openems.edge.bridge.modbus.api.task.FC6WriteRegisterTask; +import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.channel.EnumReadChannel; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.sum.GridMode; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.common.type.TypeUtils; +import io.openems.edge.ess.api.HybridEss; +import io.openems.edge.ess.api.SymmetricEss; +import io.openems.edge.goodwe.charger.AbstractGoodWeEtCharger; +import io.openems.edge.goodwe.common.enums.BatteryMode; +import io.openems.edge.goodwe.common.enums.GoodweType; + +public abstract class AbstractGoodWe extends AbstractOpenemsModbusComponent implements GoodWe, OpenemsComponent { + + private final Logger log = LoggerFactory.getLogger(AbstractGoodWe.class); + + protected final Set chargers = new HashSet<>(); + + public AbstractGoodWe(io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsNamedException { + super(firstInitialChannelIds, furtherInitialChannelIds); + } + + @Override + protected final ModbusProtocol defineModbusProtocol() throws OpenemsException { + return new ModbusProtocol(this, // + + new FC3ReadRegistersTask(35001, Priority.LOW, // + m(SymmetricEss.ChannelId.MAX_APPARENT_POWER, new UnsignedWordElement(35001)), // + new DummyRegisterElement(35002), // + m(GoodWe.ChannelId.SERIAL_NUMBER, new StringWordElement(35003, 8)), // + m(GoodWe.ChannelId.GOODWE_TYPE, new StringWordElement(35011, 5), new ElementToChannelConverter( + // element -> channel + value -> { + // Evaluate GoodweType + final GoodweType result; + if (value == null) { + result = GoodweType.UNDEFINED; + } else { + String stringValue = TypeUtils.getAsType(OpenemsType.STRING, value); + switch (stringValue) { + case "GW10K-BT": + result = GoodweType.GOODWE_10K_BT; + break; + case "GW8K-BT": + result = GoodweType.GOODWE_8K_BT; + break; + case "GW5K-BT": + result = GoodweType.GOODWE_5K_BT; + break; + case "GW10K-ET": + result = GoodweType.GOODWE_10K_ET; + break; + case "GW8K-ET": + result = GoodweType.GOODWE_8K_ET; + break; + case "GW5K-ET": + result = GoodweType.GOODWE_5K_ET; + break; + default: + this.logInfo(this.log, "Unable to identify GoodWe by name [" + value + "]"); + result = GoodweType.UNDEFINED; + break; + } + } + // Log on first occurrence + if (result != this.getGoodweType()) { + switch (result) { + case GOODWE_10K_BT: + case GOODWE_8K_BT: + case GOODWE_5K_BT: + case GOODWE_10K_ET: + case GOODWE_8K_ET: + case GOODWE_5K_ET: + this.logInfo(this.log, "Identified " + result.getName()); + break; + case UNDEFINED: + break; + } + } + return result; + }, // + + // channel -> element + value -> value))), + + new FC3ReadRegistersTask(35111, Priority.LOW, // + m(GoodWe.ChannelId.V_PV3, new UnsignedWordElement(35111), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.I_PV3, new UnsignedWordElement(35112), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + new DummyRegisterElement(35113, 35114), // + m(GoodWe.ChannelId.V_PV4, new UnsignedWordElement(35115), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.I_PV4, new UnsignedWordElement(35116), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + new DummyRegisterElement(35117, 35118), // + m(GoodWe.ChannelId.PV_MODE, new UnsignedDoublewordElement(35119))), // + + new FC3ReadRegistersTask(35136, Priority.LOW, // + m(SymmetricEss.ChannelId.GRID_MODE, new UnsignedWordElement(35136), // + new ElementToChannelConverter((value) -> { + Integer intValue = TypeUtils.getAsType(OpenemsType.INTEGER, value); + if (intValue != null) { + switch (intValue) { + case 0: + return GridMode.OFF_GRID; + case 1: + return GridMode.ON_GRID; + case 2: + return GridMode.UNDEFINED; + } + } + return GridMode.UNDEFINED; + }))), // + new FC3ReadRegistersTask(35138, Priority.LOW, // + m(GoodWe.ChannelId.TOTAL_INV_POWER, new SignedWordElement(35138)), // + new DummyRegisterElement(35139), // + m(GoodWe.ChannelId.AC_ACTIVE_POWER, new SignedWordElement(35140), // + ElementToChannelConverter.INVERT), // + new DummyRegisterElement(35141), // + m(GoodWe.ChannelId.AC_REACTIVE_POWER, new SignedWordElement(35142), // + ElementToChannelConverter.INVERT), // + new DummyRegisterElement(35143), // + m(GoodWe.ChannelId.AC_APPARENT_POWER, new SignedWordElement(35144), // + ElementToChannelConverter.INVERT), // + m(GoodWe.ChannelId.BACK_UP_V_LOAD_R, new UnsignedWordElement(35145), // + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BACK_UP_I_LOAD_R, new UnsignedWordElement(35146), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BACK_UP_F_LOAD_R, new UnsignedWordElement(35147), + ElementToChannelConverter.SCALE_FACTOR_MINUS_2), // + m(GoodWe.ChannelId.LOAD_MODE_R, new UnsignedWordElement(35148)), // + new DummyRegisterElement(35149), // + m(GoodWe.ChannelId.BACK_UP_P_LOAD_R, new SignedWordElement(35150)), // + m(GoodWe.ChannelId.BACK_UP_V_LOAD_S, new UnsignedWordElement(35151), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BACK_UP_I_LOAD_S, new UnsignedWordElement(35152), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BACK_UP_F_LOAD_S, new UnsignedWordElement(35153), + ElementToChannelConverter.SCALE_FACTOR_MINUS_2), // + m(GoodWe.ChannelId.LOAD_MODE_S, new UnsignedWordElement(35154)), // + new DummyRegisterElement(35155), // + m(GoodWe.ChannelId.BACK_UP_P_LOAD_S, new SignedWordElement(35156)), // + m(GoodWe.ChannelId.BACK_UP_V_LOAD_T, new UnsignedWordElement(35157), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BACK_UP_I_LOAD_T, new UnsignedWordElement(35158), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BACK_UP_F_LOAD_T, new UnsignedWordElement(35159), + ElementToChannelConverter.SCALE_FACTOR_MINUS_2), // + m(GoodWe.ChannelId.LOAD_MODE_T, new UnsignedWordElement(35160)), // + new DummyRegisterElement(35161), // + m(GoodWe.ChannelId.BACK_UP_P_LOAD_T, new SignedWordElement(35162)), // + new DummyRegisterElement(35163), // + m(GoodWe.ChannelId.P_LOAD_R, new SignedWordElement(35164)), // + new DummyRegisterElement(35165), // + m(GoodWe.ChannelId.P_LOAD_S, new SignedWordElement(35166)), // + new DummyRegisterElement(35167), // + m(GoodWe.ChannelId.P_LOAD_T, new SignedWordElement(35168)), // + new DummyRegisterElement(35169), // + m(GoodWe.ChannelId.TOTAL_BACK_UP_LOAD, new SignedWordElement(35170)), // + new DummyRegisterElement(35171), // + m(GoodWe.ChannelId.TOTAL_LOAD_POWER, new SignedWordElement(35172)), // + m(GoodWe.ChannelId.UPS_LOAD_PERCENT, new UnsignedWordElement(35173), + ElementToChannelConverter.SCALE_FACTOR_MINUS_2)), // + + new FC3ReadRegistersTask(35180, Priority.HIGH, // + m(GoodWe.ChannelId.V_BATTERY1, new UnsignedWordElement(35180), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.I_BATTERY1, new SignedWordElement(35181), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + new DummyRegisterElement(35182), // + // required for calculation of ActivePower + m(GoodWe.ChannelId.P_BATTERY1, new SignedWordElement(35183)), + m(GoodWe.ChannelId.BATTERY_MODE, new UnsignedWordElement(35184))), // + + new FC3ReadRegistersTask(35185, Priority.LOW, // + m(GoodWe.ChannelId.WARNING_CODE, new UnsignedWordElement(35185)), // + m(GoodWe.ChannelId.SAFETY_COUNTRY, new UnsignedWordElement(35186)), // + m(GoodWe.ChannelId.WORK_MODE, new UnsignedWordElement(35187)), // + m(GoodWe.ChannelId.OPERATION_MODE, new UnsignedDoublewordElement(35188))), // + + new FC3ReadRegistersTask(35206, Priority.LOW, // + m(this.getDcChargeEnergyChannel(), new UnsignedDoublewordElement(35206), // + ElementToChannelConverter.SCALE_FACTOR_2), // + new DummyRegisterElement(35208), // + m(this.getDcDischargeEnergyChannel(), new UnsignedDoublewordElement(35209), + ElementToChannelConverter.SCALE_FACTOR_2)), // + + new FC3ReadRegistersTask(36003, Priority.LOW, // + m(GoodWe.ChannelId.B_METER_COMMUNICATE_STATUS, new UnsignedWordElement(36003)), // + m(GoodWe.ChannelId.METER_COMMUNICATE_STATUS, new UnsignedWordElement(36004))), // + + new FC3ReadRegistersTask(37001, Priority.HIGH, + m(GoodWe.ChannelId.BATTERY_TYPE_INDEX, new UnsignedWordElement(37001)), // + m(GoodWe.ChannelId.BMS_STATUS, new UnsignedWordElement(37002)), // + m(GoodWe.ChannelId.BMS_PACK_TEMPERATURE, new UnsignedWordElement(37003), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BMS_CHARGE_IMAX, new UnsignedWordElement(37004)), // + m(GoodWe.ChannelId.BMS_DISCHARGE_IMAX, new UnsignedWordElement(37005)), // + new DummyRegisterElement(37006), // + this.getSocModbusElement(37007), // + m(GoodWe.ChannelId.BMS_SOH, new UnsignedWordElement(37008)), // + m(GoodWe.ChannelId.BMS_BATTERY_STRINGS, new UnsignedWordElement(37009))), // + + new FC16WriteRegistersTask(45350, // + m(GoodWe.ChannelId.LEAD_BAT_CAPACITY, new UnsignedWordElement(45350), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BATT_STRINGS, new UnsignedWordElement(45351)), // + m(GoodWe.ChannelId.BATT_CHARGE_VOLT_MAX, new UnsignedWordElement(45352), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), + m(GoodWe.ChannelId.BATT_CHARGE_CURR_MAX, new UnsignedWordElement(45353), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + new FC6WriteRegisterTask(45354, // + m(GoodWe.ChannelId.BATT_VOLT_UNDER_MIN, new UnsignedWordElement(45354), // + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + new FC16WriteRegistersTask(45355, // + m(GoodWe.ChannelId.BATT_DISCHARGE_CURR_MAX, new UnsignedWordElement(45355), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + + new FC3ReadRegistersTask(45350, Priority.LOW, // + m(GoodWe.ChannelId.LEAD_BAT_CAPACITY, new UnsignedWordElement(45350), // + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.BATT_STRINGS, new UnsignedWordElement(45351)), // + m(GoodWe.ChannelId.BATT_CHARGE_VOLT_MAX, new UnsignedWordElement(45352), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), + m(GoodWe.ChannelId.BATT_CHARGE_CURR_MAX, new UnsignedWordElement(45353), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + new FC3ReadRegistersTask(45354, Priority.HIGH, // + m(GoodWe.ChannelId.BATT_VOLT_UNDER_MIN, new UnsignedWordElement(45354), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + new FC3ReadRegistersTask(45355, Priority.HIGH, // + m(GoodWe.ChannelId.BATT_DISCHARGE_CURR_MAX, new UnsignedWordElement(45355), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + + new FC16WriteRegistersTask(47000, // + m(GoodWe.ChannelId.APP_MODE_INDEX, new UnsignedWordElement(47000)), // + m(GoodWe.ChannelId.METER_CHECK_VALUE, new UnsignedWordElement(47001)), // + m(GoodWe.ChannelId.WMETER_CONNECT_CHECK_FLAG, new UnsignedWordElement(47002))), // + + new FC3ReadRegistersTask(47000, Priority.LOW, // + m(GoodWe.ChannelId.APP_MODE_INDEX, new UnsignedWordElement(47000)), // + m(GoodWe.ChannelId.METER_CHECK_VALUE, new UnsignedWordElement(47001)), // + m(GoodWe.ChannelId.WMETER_CONNECT_CHECK_FLAG, new UnsignedWordElement(47002))), // + + new FC16WriteRegistersTask(47500, // + m(GoodWe.ChannelId.STOP_SOC_PROTECT, new UnsignedWordElement(47500)), // + new DummyRegisterElement(47501, 47508), // + m(GoodWe.ChannelId.FEED_POWER_ENABLE, new UnsignedWordElement(47509)), // + m(GoodWe.ChannelId.FEED_POWER_PARA, new UnsignedWordElement(47510)), // + m(GoodWe.ChannelId.EMS_POWER_MODE, new UnsignedWordElement(47511)), // + m(GoodWe.ChannelId.EMS_POWER_SET, new UnsignedWordElement(47512))), // + + new FC16WriteRegistersTask(47531, // + m(GoodWe.ChannelId.SOC_START_TO_FORCE_CHARGE, new UnsignedWordElement(47531)), // + m(GoodWe.ChannelId.SOC_STOP_TO_FORCE_CHARGE, new UnsignedWordElement(47532)), // + m(GoodWe.ChannelId.CLEAR_ALL_ECONOMIC_MODE, new UnsignedWordElement(47533))), // + + new FC3ReadRegistersTask(47500, Priority.LOW, + m(GoodWe.ChannelId.STOP_SOC_PROTECT, new UnsignedWordElement(47500)), // + new DummyRegisterElement(47501, 47508), // + m(GoodWe.ChannelId.FEED_POWER_ENABLE, new UnsignedWordElement(47509)), // + m(GoodWe.ChannelId.FEED_POWER_PARA, new UnsignedWordElement(47510))), // + + new FC3ReadRegistersTask(47511, Priority.LOW, + m(GoodWe.ChannelId.EMS_POWER_MODE, new UnsignedWordElement(47511)), // + m(GoodWe.ChannelId.EMS_POWER_SET, new UnsignedWordElement(47512))), // + + new FC3ReadRegistersTask(47531, Priority.LOW, + m(GoodWe.ChannelId.SOC_START_TO_FORCE_CHARGE, new UnsignedWordElement(47531)), // + m(GoodWe.ChannelId.SOC_STOP_TO_FORCE_CHARGE, new UnsignedWordElement(47532))), // + + new FC6WriteRegisterTask(47906, // + m(GoodWe.ChannelId.WBMS_BAT_VOLTAGE, new UnsignedWordElement(47906), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + new FC6WriteRegisterTask(47907, // + m(GoodWe.ChannelId.WBMS_BAT_CURRENT, new UnsignedWordElement(47907))), // + new FC6WriteRegisterTask(47908, // + m(GoodWe.ChannelId.WBMS_BAT_SOC, new UnsignedWordElement(47908))), // + new FC6WriteRegisterTask(47909, // + m(GoodWe.ChannelId.WBMS_BAT_SOH, new UnsignedWordElement(47909))), // + new FC6WriteRegisterTask(47910, // + m(GoodWe.ChannelId.WBMS_BAT_TEMPERATURE, new SignedWordElement(47910), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1)), // + + new FC3ReadRegistersTask(47900, Priority.LOW, // + m(GoodWe.ChannelId.BMS_VERSION, new UnsignedWordElement(47900)), // + m(GoodWe.ChannelId.BATT_STRINGS_RS485, new UnsignedWordElement(47901)), // + m(GoodWe.ChannelId.WBMS_BAT_CHARGE_VMAX, new UnsignedWordElement(47902), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.WBMS_BAT_CHARGE_IMAX, new UnsignedWordElement(47903), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.WBMS_BAT_DISCHARGE_VMIN, new UnsignedWordElement(47904), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.WBMS_BAT_DISCHARGE_IMAX, new UnsignedWordElement(47905), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.WBMS_BAT_VOLTAGE, new UnsignedWordElement(47906), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(GoodWe.ChannelId.WBMS_BAT_CURRENT, new UnsignedWordElement(47907)), // + m(GoodWe.ChannelId.WBMS_BAT_SOC, new UnsignedWordElement(47908)), // + m(GoodWe.ChannelId.WBMS_BAT_SOH, new UnsignedWordElement(47909)), // + m(GoodWe.ChannelId.WBMS_BAT_TEMPERATURE, new SignedWordElement(47910), + ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // + m(new BitsWordElement(47911, this) // + .bit(0, GoodWe.ChannelId.STATE_58) // + .bit(1, GoodWe.ChannelId.STATE_59) // + .bit(2, GoodWe.ChannelId.STATE_60) // + .bit(3, GoodWe.ChannelId.STATE_61) // + .bit(4, GoodWe.ChannelId.STATE_62) // + .bit(5, GoodWe.ChannelId.STATE_63) // + .bit(6, GoodWe.ChannelId.STATE_64) // + .bit(7, GoodWe.ChannelId.STATE_65) // + .bit(8, GoodWe.ChannelId.STATE_66) // + .bit(9, GoodWe.ChannelId.STATE_67) // + .bit(10, GoodWe.ChannelId.STATE_68) // + .bit(11, GoodWe.ChannelId.STATE_69)), // + new DummyRegisterElement(47912), // + m(new BitsWordElement(47913, this) // + .bit(0, GoodWe.ChannelId.STATE_42) // + .bit(1, GoodWe.ChannelId.STATE_43) // + .bit(2, GoodWe.ChannelId.STATE_44) // + .bit(3, GoodWe.ChannelId.STATE_45) // + .bit(4, GoodWe.ChannelId.STATE_46) // + .bit(5, GoodWe.ChannelId.STATE_47) // + .bit(6, GoodWe.ChannelId.STATE_48) // + .bit(7, GoodWe.ChannelId.STATE_49) // + .bit(8, GoodWe.ChannelId.STATE_50) // + .bit(9, GoodWe.ChannelId.STATE_51) // + .bit(10, GoodWe.ChannelId.STATE_52) // + .bit(11, GoodWe.ChannelId.STATE_53) // + .bit(12, GoodWe.ChannelId.STATE_54) // + .bit(13, GoodWe.ChannelId.STATE_55) // + .bit(14, GoodWe.ChannelId.STATE_56) // + .bit(15, GoodWe.ChannelId.STATE_57)), // + new DummyRegisterElement(47914), // + m(new BitsWordElement(47915, this) // + .bit(0, GoodWe.ChannelId.STATE_79) // + .bit(1, GoodWe.ChannelId.STATE_80) // + .bit(2, GoodWe.ChannelId.STATE_81)))); + } + + protected AbstractModbusElement getSocModbusElement(int address) throws NotImplementedException { + if (this instanceof HybridEss) { + return m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(address), new ElementToChannelConverter( + // element -> channel + value -> { + // Set SoC to undefined if there is No Battery + EnumReadChannel batteryModeChannel = this.channel(GoodWe.ChannelId.BATTERY_MODE); + BatteryMode batteryMode = batteryModeChannel.value().asEnum(); + if (batteryMode == BatteryMode.NO_BATTERY || batteryMode == BatteryMode.UNDEFINED) { + return null; + } else { + return value; + } + }, + // channel -> element + value -> value)); + } else if (this instanceof HybridManagedSymmetricBatteryInverter) { + return new DummyRegisterElement(address); + } else { + throw new NotImplementedException("Wrong implementation of AbstractGoodWe"); + } + } + + private io.openems.edge.common.channel.ChannelId getDcDischargeEnergyChannel() throws NotImplementedException { + if (this instanceof HybridEss) { + return HybridEss.ChannelId.DC_DISCHARGE_ENERGY; + } else if (this instanceof HybridManagedSymmetricBatteryInverter) { + return HybridManagedSymmetricBatteryInverter.ChannelId.DC_DISCHARGE_ENERGY; + } else { + throw new NotImplementedException("Wrong implementation of AbstractGoodWe"); + } + } + + private io.openems.edge.common.channel.ChannelId getDcChargeEnergyChannel() throws OpenemsException { + if (this instanceof HybridEss) { + return HybridEss.ChannelId.DC_CHARGE_ENERGY; + } else if (this instanceof HybridManagedSymmetricBatteryInverter) { + return HybridManagedSymmetricBatteryInverter.ChannelId.DC_CHARGE_ENERGY; + } else { + throw new NotImplementedException("Wrong implementation of AbstractGoodWe"); + } + } + + @Override + public final void addCharger(AbstractGoodWeEtCharger charger) { + this.chargers.add(charger); + } + + @Override + public final void removeCharger(AbstractGoodWeEtCharger charger) { + this.chargers.remove(charger); + } + + /** + * Gets the PV production from chargers ACTUAL_POWER. Returns null if the PV + * production is not available. + * + * @return production power + */ + protected final Integer calculatePvProduction() { + Integer productionPower = null; + for (AbstractGoodWeEtCharger charger : this.chargers) { + productionPower = TypeUtils.sum(productionPower, charger.getActualPower().get()); + } + return productionPower; + } + + /** + * Calculates the ActivePower from P_BATTERY1 and chargers ACTUAL_POWER. + * + * @return active power + */ + protected final Integer calculateActivePower() { + Integer productionPower = this.calculatePvProduction(); + final Channel batteryPower = this.channel(GoodWe.ChannelId.P_BATTERY1); + return TypeUtils.sum(productionPower, batteryPower.value().get()); + } + +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/GoodWe.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/GoodWe.java new file mode 100644 index 00000000000..9fc9bdd0ace --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/GoodWe.java @@ -0,0 +1,736 @@ +package io.openems.edge.goodwe.common; + +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.channel.Unit; +import io.openems.common.types.OpenemsType; +import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.channel.value.Value; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.goodwe.charger.AbstractGoodWeEtCharger; +import io.openems.edge.goodwe.charger.GoodWeChargerPv1; +import io.openems.edge.goodwe.charger.GoodWeChargerPv2; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine; +import io.openems.edge.goodwe.common.enums.AppModeIndex; +import io.openems.edge.goodwe.common.enums.BatteryMode; +import io.openems.edge.goodwe.common.enums.GoodweType; +import io.openems.edge.goodwe.common.enums.LoadMode; +import io.openems.edge.goodwe.common.enums.MeterCommunicateStatus; +import io.openems.edge.goodwe.common.enums.MeterConnectCheckFlag; +import io.openems.edge.goodwe.common.enums.MeterConnectStatus; +import io.openems.edge.goodwe.common.enums.OperationMode; +import io.openems.edge.goodwe.common.enums.OutputTypeAC; +import io.openems.edge.goodwe.common.enums.PowerModeEms; +import io.openems.edge.goodwe.common.enums.SafetyCountry; +import io.openems.edge.goodwe.common.enums.WorkMode; + +public interface GoodWe extends OpenemsComponent { + + /** + * Registers a GoodWe Charger. + * + * @param charger either {@link GoodWeChargerPv1} or {@link GoodWeChargerPv2} + */ + public void addCharger(AbstractGoodWeEtCharger charger); + + /** + * Unregisters a GoodWe Charger. + * + * @param charger either {@link GoodWeChargerPv1} or {@link GoodWeChargerPv2} + */ + public void removeCharger(AbstractGoodWeEtCharger charger); + + public static enum ChannelId implements io.openems.edge.common.channel.ChannelId { + APPLY_POWER_STATE_MACHINE(Doc.of(ApplyPowerStateMachine.State.values())), + + MODBUS_PROTOCOL_VERSION(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + RATED_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + AC_OUTPUT_TYPE(Doc.of(OutputTypeAC.values())), // + SERIAL_NUMBER(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + GOODWE_TYPE(Doc.of(GoodweType.values()) // + .accessMode(AccessMode.READ_ONLY)), // + DSP1_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + DSP2_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + DSP_SPN_VERSION(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + ARM_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + ARM_SVN_VERSION(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + DSP_INTERNAL_FIRMWARE_VERSION(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + ARM_INTERNAL_FIRMWARE_VERSION(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + SIMCCID(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + + // Running Data + RTC_YEAR_MONTH(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + RTC_DATE_HOUR(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + RTC_MINUTE_SECOND(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), + V_PV3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), + I_PV3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), + P_PV3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), + V_PV4(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), + I_PV4(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), + P_PV4(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), + PV_MODE(Doc.of(WorkMode.values())), // + TOTAL_INV_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + AC_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + AC_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT_AMPERE_REACTIVE).accessMode(AccessMode.READ_ONLY)), // + AC_APPARENT_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT_AMPERE).accessMode(AccessMode.READ_ONLY)), // + BACK_UP_V_LOAD_R(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), + BACK_UP_I_LOAD_R(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), + BACK_UP_F_LOAD_R(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_ONLY)), + LOAD_MODE_R(Doc.of(LoadMode.values())), // + BACK_UP_P_LOAD_R(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), + BACK_UP_V_LOAD_S(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), + BACK_UP_I_LOAD_S(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), + BACK_UP_F_LOAD_S(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_ONLY)), + LOAD_MODE_S(Doc.of(LoadMode.values())), // + BACK_UP_P_LOAD_S(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), + BACK_UP_V_LOAD_T(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), + BACK_UP_I_LOAD_T(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), + BACK_UP_F_LOAD_T(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_ONLY)), + LOAD_MODE_T(Doc.of(LoadMode.values())), // + BACK_UP_P_LOAD_T(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), + P_LOAD_R(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + P_LOAD_S(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + P_LOAD_T(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + TOTAL_BACK_UP_LOAD(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + TOTAL_LOAD_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + UPS_LOAD_PERCENT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_ONLY)), // + AIR_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // + MODULE_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // + RADIATOR_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // + FUNCTION_BIT_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + BUS_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // + NBUS_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // + V_BATTERY1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // + I_BATTERY1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // + P_BATTERY1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + BATTERY_MODE(Doc.of(BatteryMode.values())), // + WARNING_CODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + SAFETY_COUNTRY(Doc.of(SafetyCountry.values())), // . + WORK_MODE(Doc.of(WorkMode.values())), // + OPERATION_MODE(Doc.of(OperationMode.values())), // + + // Error Message + STATE_0(Doc.of(Level.FAULT).text("The GFCI detecting circuit is abnormal")), // + STATE_1(Doc.of(Level.FAULT).text("The output current sensor is abnormal")), // + STATE_2(Doc.of(Level.WARNING).text("TBD")), // + STATE_3(Doc.of(Level.FAULT).text("DCI Consistency Failure")), // + STATE_4(Doc.of(Level.FAULT).text("GFCI Consistency Failure")), // + STATE_5(Doc.of(Level.WARNING).text("TBD")), // + STATE_6(Doc.of(Level.FAULT).text("GFCI Device Failure")), // + STATE_7(Doc.of(Level.FAULT).text("Relay Device Failure")), // + STATE_8(Doc.of(Level.FAULT).text("AC HCT Failure")), // + STATE_9(Doc.of(Level.FAULT).text("Utility Loss")), // + STATE_10(Doc.of(Level.FAULT).text("Gournd I Failure")), // + STATE_11(Doc.of(Level.WARNING).text("DC Bus High")), // + STATE_12(Doc.of(Level.FAULT).text("Internal Fan Failure(Back-Up Over Load for ES)")), // + STATE_13(Doc.of(Level.WARNING).text("Over Temperature")), // + STATE_14(Doc.of(Level.FAULT).text("Auto Test Failure")), // + STATE_15(Doc.of(Level.WARNING).text("PV Over Voltage")), // + STATE_16(Doc.of(Level.FAULT).text("External Fan Failure")), // + STATE_17(Doc.of(Level.FAULT).text("Vac Failure")), // + STATE_18(Doc.of(Level.FAULT).text("Isolation Failure")), // + STATE_19(Doc.of(Level.WARNING).text("DC Injection High")), // + STATE_20(Doc.of(Level.WARNING).text("TBD")), // + STATE_21(Doc.of(Level.WARNING).text("TBD")), // + STATE_22(Doc.of(Level.FAULT).text("Fac Consistency Failure")), // + STATE_23(Doc.of(Level.FAULT).text("Vac Consistency Failure")), // + STATE_24(Doc.of(Level.WARNING).text("TBD")), // + STATE_25(Doc.of(Level.WARNING).text("Relay Check Failure")), // + STATE_26(Doc.of(Level.WARNING).text("TBD")), // + STATE_27(Doc.of(Level.WARNING).text("TBD")), // + STATE_28(Doc.of(Level.WARNING).text("TBD")), // + STATE_29(Doc.of(Level.FAULT).text("Fac Failure")), // + STATE_30(Doc.of(Level.FAULT).text("EEPROM R/W Failure")), // + STATE_31(Doc.of(Level.FAULT).text("Internal Communication Failure")), // + + PV_E_TOTAL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + PV_E_DAY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_TOTAL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + H_TOTAL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HOUR).accessMode(AccessMode.READ_ONLY)), // + E_DAY_SELL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_TOTAL_BUY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_DAY_BUY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_TOTAL_LOAD(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_LOAD_DAY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_BATTERY_CHARGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_CHARGE_DAY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_BATTERY_DISCHARGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_DISCHARGE_DAY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + BATT_STRINGS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + CPLD_WARNING_CODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + W_CHARGER_CTRL_FLAG(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + DERATE_FLAG(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + DERATE_FROZEN_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + DIAG_STATUS_H(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + DIAG_STATUS_L(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + + // External Communication Data (ARM) + COM_MODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + RSSI(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + MANIFACTURE_CODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + B_METER_COMMUNICATE_STATUS(Doc.of(MeterConnectStatus.values())), // + METER_COMMUNICATE_STATUS(Doc.of(MeterCommunicateStatus.values())), // + MT_ACTIVE_POWER_R(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + MT_ACTIVE_POWER_S(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + MT_ACTIVE_POWER_T(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + MT_TOTAL_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + MT_TOTAL_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // + METER_PF_R(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + METER_PF_S(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + METER_PF_T(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + METER_POWER_FACTOR(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + METER_FREQUENCE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + E_TOTAL_SELL(Doc.of(OpenemsType.FLOAT) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + E_TOTAL_BUY2(Doc.of(OpenemsType.FLOAT) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // + + STATE_32(Doc.of(Level.WARNING).text("DRM0")), // + STATE_33(Doc.of(Level.WARNING).text("DRM1")), // + STATE_34(Doc.of(Level.WARNING).text("DRM2")), // + STATE_35(Doc.of(Level.WARNING).text("DRM3")), // + STATE_36(Doc.of(Level.WARNING).text("DRM4")), // + STATE_37(Doc.of(Level.WARNING).text("DRM5")), // + STATE_38(Doc.of(Level.WARNING).text("DRM6")), // + STATE_39(Doc.of(Level.WARNING).text("DRM7")), // + STATE_40(Doc.of(Level.WARNING).text("DRM8")), // + STATE_41(Doc.of(Level.WARNING).text("DRED Connect Status")), // + + BATTERY_TYPE_INDEX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + BMS_STATUS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + BMS_PACK_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // + BMS_CHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + BMS_DISCHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + + STATE_42(Doc.of(Level.WARNING).text("Charging over-voltage2")), // + STATE_43(Doc.of(Level.WARNING).text("Discharging under-voltage2")), // + STATE_44(Doc.of(Level.WARNING).text("CellHigh temperature2")), // + STATE_45(Doc.of(Level.WARNING).text("CellLow temperature2")), // + STATE_46(Doc.of(Level.WARNING).text("Charging overcurrent2")), // + STATE_47(Doc.of(Level.WARNING).text("Discharging overcurrent2")), // + STATE_48(Doc.of(Level.WARNING).text("Precharge fault")), // + STATE_49(Doc.of(Level.WARNING).text("DC bus fault")), // + STATE_50(Doc.of(Level.WARNING).text("Battery break")), // + STATE_51(Doc.of(Level.WARNING).text("Battery Lock")), // + STATE_52(Doc.of(Level.WARNING).text("Discharge circuit Fault")), // + STATE_53(Doc.of(Level.WARNING).text("Charging circuit Failure")), // + STATE_54(Doc.of(Level.WARNING).text("Communication failure2")), // + STATE_55(Doc.of(Level.WARNING).text("Cell High temperature3")), // + STATE_56(Doc.of(Level.WARNING).text("Discharging under-voltage3")), // + STATE_57(Doc.of(Level.WARNING).text("Charging over-voltage3")), // + + BMS_SOH(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_ONLY)), + BMS_BATTERY_STRINGS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + + STATE_58(Doc.of(Level.WARNING).text("Charging over-voltage1")), // + STATE_59(Doc.of(Level.WARNING).text("Discharging under-voltage1")), // + STATE_60(Doc.of(Level.WARNING).text("Cell High temperature1")), // + STATE_61(Doc.of(Level.WARNING).text("Cell Low temperature1")), // + STATE_62(Doc.of(Level.WARNING).text("Charging over-current1")), // + STATE_63(Doc.of(Level.WARNING).text("Discharging over-current1")), // + STATE_64(Doc.of(Level.WARNING).text("communication failure1")), // + STATE_65(Doc.of(Level.WARNING).text("System Reboot")), // + STATE_66(Doc.of(Level.WARNING).text("Cell- imbalance")), // + STATE_67(Doc.of(Level.WARNING).text("System Low temperature1")), // + STATE_68(Doc.of(Level.WARNING).text("System Low temperature2")), // + STATE_69(Doc.of(Level.WARNING).text("System High temperature")), // + + BATTERY_PROTOCOL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + + // Setting Parameter + USER_PASSWORD1(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + USER_PASSWORD2(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + USER_PASSWORD3(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + ROUTER_SSID(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + ROUTER_PASSWORD(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + ROUTER_ENCRYPTION_METHOD(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DOMAIN1(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + PORT_NUMBER1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DOMAIN2(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + PORT_NUMBER2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + MODBUS_ADDRESS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + MODBUS_MANUFACTURER(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + MODBUS_BADRATE_485(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + RTC_YEAR_MONTH_2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + RTC_DAY_HOUR_2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + RTC_MINUTE_SECOND_2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + SERIAL_NUMBER_2(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DEVICE_TYPE_2(Doc.of(OpenemsType.STRING) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + RESUME_FACTORY_SETTING(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + CLEAR_DATA(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + START(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + STOP(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + RESET(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + RESET_SPS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + PV_E_TOTAL_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + PV_E_DAY_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_TOTAL_SELL_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + H_TOTAL_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HOUR).accessMode(AccessMode.READ_WRITE)), // + E_DAY_SELL_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_TOTAL_BUY_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_DAY_BUY_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_TOTAL_LOAD_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_LOAD_DAY_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_BATTERY_CHARGE_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_CHARGE_DAY_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_BATTERY_DISCHARGE_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + E_DISCHARGE_DAY_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // + LANGUAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + SAFETY_COUNTRY_CODE(Doc.of(SafetyCountry.values())), // + ISO(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + LVRT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + ISLANDING(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BURN_IN_RESET_TIME(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.MINUTE).accessMode(AccessMode.READ_WRITE)), // + PV_START_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + ENABLE_MPPT_4SHADOW(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BACK_UP_ENABLE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + AUTO_START_BACKUP(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + GRID_WAVE_CHECK_LEVEL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + REPAID_CUT_OFF(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BACKUP_START_DLY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + UPS_STD_VOLT_TYPE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + UNDER_ATS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BURN_IN_MODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BACKUP_OVERLOAD_DELAY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + UPSPHASE_TYPE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DERATE_RATE_VDE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + LEAD_BAT_CAPACITY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATTERY_STRINGS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATT_CHARGE_VOLT_MAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATT_CHARGE_CURR_MAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATT_VOLT_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + BATT_DISCHARGE_CURR_MAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATT_SOC_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATT_OFF_LINE_VOLT_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATT_OFFLINE_SOC_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + CLEAR_BATTERY_SETTING(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + + // CosPhi curve + ENABLE_CURVE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + POINT_A_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + POINT_A_PF(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + POINT_B_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + POINT_B_PF(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + POINT_C_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + POINT_C_PF(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + LOCK_IN_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + LOCK_OUT_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + LOCK_OUT_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + + // Power and frequency curve + STATE_70(Doc.of(Level.INFO).text("ON/OFF")), // + STATE_71(Doc.of(Level.INFO).text("response mode")), // + + FFROZEN_DCH(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + FFROZEN_CH(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + FSTOP_DCH(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + FSTOP_CH(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + RECOVERY_WAITING_TIME(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.SECONDS).accessMode(AccessMode.READ_WRITE)), // + RECOVERY_FREQURNCY1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + RECOVERY_FREQUENCY2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + RECOVERY_SLOPE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + FFROZEN_DCH_SLOPE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + FFROZEN_CH_SLOPE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // + DOWN_SLOPE_POWER_REFERENCE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DOWN_SLOP(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + + // QU curve + ENABLE_CURVE_QU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + LOCK_IN_POWER_QU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + LOCK_OUT_POWER_QU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + V1_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V1_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + V2_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V2_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + V3_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V3_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + V4_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V4_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + K_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + TIME_CONSTANT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + MISCELLANEA(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + RATED_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + RESPONSE_TIME(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + + // PU curve + PU_CURVE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + POWER_CHANGE_RATE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + V1_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V1_VALUE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + V2_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V2_VALUE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + V3_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V3_VALUE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + V4_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + V4_VALUE_PU(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + FIXED_POWER_FACTOR(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + FIXED_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + FIXED_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + GRID_LIMIT_BY_VOLT_START_VOL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + GRID_LIMIT_BY_VOLT_START_PER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + GRID_LIMIT_BY_VOLT_SLOPE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.WRITE_ONLY)), // + AUTO_TEST_ENABLE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + AUTO_TEST_STEP(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + UW_ITALY_FREQ_MODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + + // Meter Control ARM + APP_MODE_INDEX(Doc.of(AppModeIndex.values())), // + METER_CHECK_VALUE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + WMETER_CONNECT_CHECK_FLAG(Doc.of(MeterConnectCheckFlag.values())), // + SIMULATE_METER_POWER(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BREEZE_ON_OFF(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + LOG_DATA_ENABLE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DATA_SEND_INTERVAL(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.SECONDS).accessMode(AccessMode.READ_WRITE)), // + + // Battery Control Data ARM + STOP_SOC_PROTECT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATTERY_FLOAT_VOLT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + BATTERY_FLOAT_CURRENT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_WRITE)), // + BATTERY_FLOAT_TIME(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.MINUTE).accessMode(AccessMode.READ_WRITE)), // + BATTERY_TYPE_INDEX_ARM(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + MANUFACTURE_CODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + DC_VOLT_OUTPUT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BAT_AVG_CHG_VOLT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + BAT_AVG_CHG_HOURS(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.HOUR).accessMode(AccessMode.READ_WRITE)), // + FEED_POWER_ENABLE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + FEED_POWER_PARA(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // + EMS_POWER_MODE(Doc.of(PowerModeEms.values()) // + .accessMode(AccessMode.READ_WRITE)), // + EMS_POWER_SET(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BAT_BMS_CURR_LMT_COFF(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BATTERY_PROTOCOL_ARM(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + START_TIME_1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + END_TIME_1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BAT_POWER_PERCENT_1(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + + STATE_72(Doc.of(Level.INFO).text("SUNDAY")), // + STATE_73(Doc.of(Level.WARNING).text("MONDAY")), // + STATE_74(Doc.of(Level.WARNING).text("TUESDAY")), // + STATE_75(Doc.of(Level.WARNING).text("Wednesday")), // + STATE_76(Doc.of(Level.WARNING).text("Thursday")), // + STATE_77(Doc.of(Level.WARNING).text("Friday")), // + STATE_78(Doc.of(Level.WARNING).text("Saturday")), // + + START_TIME_2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + END_TIME_2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BAT_POWER_PERCENT_2(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + START_TIME_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + END_TIME_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BAT_POWER_PERCENT_3(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + START_TIME_4(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + END_TIME_4(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + BAT_POWER_PERCENT_4(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + SOC_START_TO_FORCE_CHARGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + SOC_STOP_TO_FORCE_CHARGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // + CLEAR_ALL_ECONOMIC_MODE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // + + // BMS for RS485 + BMS_VERSION(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + BATT_STRINGS_RS485(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // + WBMS_BAT_CHARGE_VMAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // + WBMS_BAT_CHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), // + WBMS_BAT_DISCHARGE_VMIN(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // + WBMS_BAT_DISCHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), // + WBMS_BAT_VOLTAGE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // + WBMS_BAT_CURRENT(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.AMPERE).accessMode(AccessMode.READ_WRITE)), // + WBMS_BAT_SOC(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + WBMS_BAT_SOH(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // + WBMS_BAT_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_WRITE)), // + + // BMS_STATUS(), // + STATE_79(Doc.of(Level.INFO).text("force to charge")), // + STATE_80(Doc.of(Level.INFO).text("Stop charging")), // TODO can be removed? + STATE_81(Doc.of(Level.INFO).text("Stop discharging")); + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + @Override + public Doc doc() { + return this.doc; + } + } + + /** + * Gets the Channel for {@link ChannelId#GOODWE_TYPE}. + * + * @return the Channel + */ + public default Channel getGoodweTypeChannel() { + return this.channel(GoodWe.ChannelId.GOODWE_TYPE); + } + + /** + * Gets the Device Type. See {@link ChannelId#GOODWE_TYPE}. + * + * @return the Channel {@link Value} + */ + public default GoodweType getGoodweType() { + return this.getGoodweTypeChannel().value().asEnum(); + } + +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/ApplyPowerStateMachine.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/ApplyPowerStateMachine.java similarity index 87% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/ApplyPowerStateMachine.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/ApplyPowerStateMachine.java index 544a0646e89..8fc4b45784c 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/ApplyPowerStateMachine.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/ApplyPowerStateMachine.java @@ -1,9 +1,9 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.common.types.OptionsEnum; import io.openems.edge.common.statemachine.AbstractStateMachine; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.enums.GoodweType; +import io.openems.edge.goodwe.common.enums.GoodweType; public class ApplyPowerStateMachine extends AbstractStateMachine { @@ -100,11 +100,12 @@ public StateHandler getStateHandler(State state) { /** * Evaluates the State we are currently in. * - * @param readOnlyMode - * @param pvProduction - * @param soc - * @param activePowerSetPoint - * @return + * @param goodweType the {@link GoodweType} + * @param readOnlyMode the configured readOnlyMode + * @param pvProduction the current PV production + * @param soc the state-of-charge of the battery + * @param activePowerSetPoint the set-point for ActivePower + * @return the appropriate {@link State} */ public static State evaluateState(GoodweType goodweType, boolean readOnlyMode, int pvProduction, int soc, int activePowerSetPoint) { @@ -115,6 +116,8 @@ public static State evaluateState(GoodweType goodweType, boolean readOnlyMode, i } else { switch (goodweType) { case GOODWE_10K_BT: + case GOODWE_8K_BT: + case GOODWE_5K_BT: if (activePowerSetPoint > 0) { // Set-Point is positive return State.BT_DISCHARGE; @@ -124,6 +127,8 @@ public static State evaluateState(GoodweType goodweType, boolean readOnlyMode, i } case GOODWE_10K_ET: + case GOODWE_8K_ET: + case GOODWE_5K_ET: if (soc > 99) { // battery is full if (activePowerSetPoint > 0) { @@ -172,9 +177,8 @@ public static State evaluateState(GoodweType goodweType, boolean readOnlyMode, i return State.UNDEFINED; } + // should not come here + return State.UNDEFINED; } - - // should not come here - return State.UNDEFINED; } -} \ No newline at end of file +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/BtChargeHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/BtChargeHandler.java similarity index 62% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/BtChargeHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/BtChargeHandler.java index ce00c57bfd7..e236224d3d4 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/BtChargeHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/BtChargeHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class BtChargeHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/BtDischargeHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/BtDischargeHandler.java similarity index 62% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/BtDischargeHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/BtDischargeHandler.java index e3698e6e6a9..794537b09a7 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/BtDischargeHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/BtDischargeHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class BtDischargeHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/Context.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/Context.java similarity index 51% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/Context.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/Context.java index 2be6e64d7f9..83b967b7c55 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/Context.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/Context.java @@ -1,10 +1,10 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.AbstractContext; -import io.openems.edge.goodwe.ess.GoodWeEssImpl; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.GoodWe; +import io.openems.edge.goodwe.common.enums.PowerModeEms; -public class Context extends AbstractContext { +public class Context extends AbstractContext { protected final int pvProduction; protected final int activePowerSetPoint; @@ -12,7 +12,7 @@ public class Context extends AbstractContext { private PowerModeEms nextPowerMode; private int essPowerSet; - public Context(GoodWeEssImpl parent, int pvProduction, int activePowerSetPoint) { + public Context(GoodWe parent, int pvProduction, int activePowerSetPoint) { super(parent); this.pvProduction = pvProduction; this.activePowerSetPoint = activePowerSetPoint; @@ -23,11 +23,21 @@ protected void setMode(PowerModeEms nextPowerMode, int essPowerSet) { this.essPowerSet = essPowerSet; } + /** + * Gets the resulting Power-Set value. + * + * @return the value + */ public int getEssPowerSet() { - return essPowerSet; + return this.essPowerSet; } + /** + * Gets the resulting {@link PowerModeEms} value. + * + * @return the value + */ public PowerModeEms getNextPowerMode() { - return nextPowerMode; + return this.nextPowerMode; } -} \ No newline at end of file +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtEmptyNegativeChargeHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtEmptyNegativeChargeHandler.java similarity index 65% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtEmptyNegativeChargeHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtEmptyNegativeChargeHandler.java index f9715dab111..504cc133d6b 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtEmptyNegativeChargeHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtEmptyNegativeChargeHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtEmptyNegativeChargeHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtEmptyPositivePvHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtEmptyPositivePvHandler.java similarity index 64% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtEmptyPositivePvHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtEmptyPositivePvHandler.java index 1637f64757f..0374b983d96 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtEmptyPositivePvHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtEmptyPositivePvHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtEmptyPositivePvHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullNegativeCurtailHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullNegativeCurtailHandler.java similarity index 61% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullNegativeCurtailHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullNegativeCurtailHandler.java index f7231e7cb24..73b9f849f20 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullNegativeCurtailHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullNegativeCurtailHandler.java @@ -1,14 +1,14 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtFullNegativeCurtailHandler extends StateHandler { @Override public State runAndGetNextState(Context context) { - + context.setMode(PowerModeEms.EXPORT_AC, 0); return State.ET_FULL_NEGATIVE_CURTAIL; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullPositiveCurtailHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullPositiveCurtailHandler.java similarity index 63% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullPositiveCurtailHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullPositiveCurtailHandler.java index 045709a0bee..f52fe90c0c5 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullPositiveCurtailHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullPositiveCurtailHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtFullPositiveCurtailHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullPositiveDischargeHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullPositiveDischargeHandler.java similarity index 65% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullPositiveDischargeHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullPositiveDischargeHandler.java index 08c8eeb54d7..8450def0556 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtFullPositiveDischargeHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtFullPositiveDischargeHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtFullPositiveDischargeHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenNegativeChargeHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenNegativeChargeHandler.java similarity index 65% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenNegativeChargeHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenNegativeChargeHandler.java index 7902440f3a2..5a59d01a5aa 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenNegativeChargeHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenNegativeChargeHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtInbetweenNegativeChargeHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenPositiveChargeHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenPositiveChargeHandler.java similarity index 65% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenPositiveChargeHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenPositiveChargeHandler.java index 6fb043faf95..77781dd5c02 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenPositiveChargeHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenPositiveChargeHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtInbetweenPositiveChargeHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenPositiveDischargeHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenPositiveDischargeHandler.java similarity index 66% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenPositiveDischargeHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenPositiveDischargeHandler.java index 144ec6e9a40..282ceb1f81b 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/EtInbetweenPositiveDischargeHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/EtInbetweenPositiveDischargeHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class EtInbetweenPositiveDischargeHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/ReadOnlyHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/ReadOnlyHandler.java similarity index 58% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/ReadOnlyHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/ReadOnlyHandler.java index 0a0917b5cc4..6ae6bbb843d 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/ReadOnlyHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/ReadOnlyHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class ReadOnlyHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/UndefinedHandler.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/UndefinedHandler.java similarity index 58% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/UndefinedHandler.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/UndefinedHandler.java index d6d91d0c2ea..02780ff5ae6 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/applypower/UndefinedHandler.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/applypower/UndefinedHandler.java @@ -1,8 +1,8 @@ -package io.openems.edge.goodwe.ess.applypower; +package io.openems.edge.goodwe.common.applypower; import io.openems.edge.common.statemachine.StateHandler; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine.State; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine.State; +import io.openems.edge.goodwe.common.enums.PowerModeEms; public class UndefinedHandler extends StateHandler { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/AppModeIndex.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/AppModeIndex.java similarity index 93% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/AppModeIndex.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/AppModeIndex.java index eb04b2314c9..a7e06b6efba 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/AppModeIndex.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/AppModeIndex.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/BatteryMode.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/BatteryMode.java similarity index 94% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/BatteryMode.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/BatteryMode.java index 8bf0b628c03..d7aabe0ad7a 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/BatteryMode.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/BatteryMode.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/GoodweType.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/GoodweType.java similarity index 62% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/GoodweType.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/GoodweType.java index c3368bfcf3f..7fb8199f33c 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/GoodweType.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/GoodweType.java @@ -1,11 +1,15 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; public enum GoodweType implements OptionsEnum { UNDEFINED(-1, "Undefined"), // - GOODWE_10K_BT(1, "GoodWe GW10K-BT"), // - GOODWE_10K_ET(2, "GoodWe GW10K-ET"); + GOODWE_10K_BT(10, "GoodWe GW10K-BT"), // + GOODWE_8K_BT(11, "GoodWe GW8K-BT"), // + GOODWE_5K_BT(12, "GoodWe GW5K-BT"), // + GOODWE_10K_ET(20, "GoodWe GW10K-ET"), // + GOODWE_8K_ET(21, "GoodWe GW8K-ET"), // + GOODWE_5K_ET(22, "GoodWe GW5K-ET"); private final int value; private final String option; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/LoadMode.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/LoadMode.java similarity index 92% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/LoadMode.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/LoadMode.java index e60143a3181..d12002177fb 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/LoadMode.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/LoadMode.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterCommunicateStatus.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterCommunicateStatus.java similarity index 92% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterCommunicateStatus.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterCommunicateStatus.java index 79d97754608..3627d0d4b69 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterCommunicateStatus.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterCommunicateStatus.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterConnectCheckFlag.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterConnectCheckFlag.java similarity index 92% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterConnectCheckFlag.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterConnectCheckFlag.java index 52f33f53663..f3e195c5af6 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterConnectCheckFlag.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterConnectCheckFlag.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterConnectStatus.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterConnectStatus.java similarity index 94% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterConnectStatus.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterConnectStatus.java index 80480cae2bb..02b59764c8d 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/MeterConnectStatus.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/MeterConnectStatus.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/OperationMode.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/OperationMode.java similarity index 94% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/OperationMode.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/OperationMode.java index c7336161e9b..2d59ba13622 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/OperationMode.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/OperationMode.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/OutputTypeAC.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/OutputTypeAC.java similarity index 93% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/OutputTypeAC.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/OutputTypeAC.java index 53e712651d9..8479d0f1ed9 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/OutputTypeAC.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/OutputTypeAC.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/PowerModeEms.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/PowerModeEms.java similarity index 96% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/PowerModeEms.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/PowerModeEms.java index 5e120c788a2..e63d31d2f25 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/PowerModeEms.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/PowerModeEms.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/SafetyCountry.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/SafetyCountry.java similarity index 98% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/SafetyCountry.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/SafetyCountry.java index 3c0216fad16..81fe1c6c962 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/SafetyCountry.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/SafetyCountry.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/WorkMode.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/WorkMode.java similarity index 94% rename from io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/WorkMode.java rename to io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/WorkMode.java index 14ee073b2fb..34d47225cf0 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/enums/WorkMode.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/enums/WorkMode.java @@ -1,4 +1,4 @@ -package io.openems.edge.goodwe.ess.enums; +package io.openems.edge.goodwe.common.enums; import io.openems.common.types.OptionsEnum; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/Config.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/Config.java index 42863606dfe..a9b5a243d3d 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/Config.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/Config.java @@ -6,7 +6,7 @@ import io.openems.edge.goodwe.GoodWeConstants; @ObjectClassDefinition(// - name = "GoodWe ET ESS", // + name = "GoodWe ESS", // description = "Implements the GoodWe ET Energy Storage System.") @interface Config { @@ -37,5 +37,5 @@ @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "GoodWe ET ESS [{id}]"; + String webconsole_configurationFactory_nameHint() default "GoodWe ESS [{id}]"; } diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEss.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEss.java index 48ac55fcf0e..2abe657a402 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEss.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEss.java @@ -1,697 +1,14 @@ package io.openems.edge.goodwe.ess; -import io.openems.common.channel.AccessMode; -import io.openems.common.channel.Level; -import io.openems.common.channel.Unit; -import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; -import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.ess.api.SymmetricEss; -import io.openems.edge.goodwe.charger.AbstractGoodWeEtCharger; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine; -import io.openems.edge.goodwe.ess.enums.AppModeIndex; -import io.openems.edge.goodwe.ess.enums.BatteryMode; -import io.openems.edge.goodwe.ess.enums.GoodweType; -import io.openems.edge.goodwe.ess.enums.LoadMode; -import io.openems.edge.goodwe.ess.enums.MeterCommunicateStatus; -import io.openems.edge.goodwe.ess.enums.MeterConnectCheckFlag; -import io.openems.edge.goodwe.ess.enums.MeterConnectStatus; -import io.openems.edge.goodwe.ess.enums.OperationMode; -import io.openems.edge.goodwe.ess.enums.OutputTypeAC; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; -import io.openems.edge.goodwe.ess.enums.SafetyCountry; -import io.openems.edge.goodwe.ess.enums.WorkMode; +import io.openems.edge.goodwe.common.GoodWe; -public interface GoodWeEss extends SymmetricEss, OpenemsComponent { - - public void addCharger(AbstractGoodWeEtCharger charger); - - public void removeCharger(AbstractGoodWeEtCharger charger); +public interface GoodWeEss extends GoodWe, SymmetricEss, OpenemsComponent { public static enum ChannelId implements io.openems.edge.common.channel.ChannelId { - APPLY_POWER_STATE_MACHINE(Doc.of(ApplyPowerStateMachine.State.values())), - - MODBUS_PROTOCOL_VERSION(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - RATED_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - AC_OUTPUT_TYPE(Doc.of(OutputTypeAC.values())), // - SERIAL_NUMBER(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - GOODWE_TYPE(Doc.of(GoodweType.values()) // - .accessMode(AccessMode.READ_ONLY)), // - DSP1_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - DSP2_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - DSP_SPN_VERSION(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - ARM_SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - ARM_SVN_VERSION(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - DSP_INTERNAL_FIRMWARE_VERSION(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - ARM_INTERNAL_FIRMWARE_VERSION(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - SIMCCID(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - - // Running Data - RTC_YEAR_MONTH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - RTC_DATE_HOUR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - RTC_MINUTE_SECOND(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), - V_PV3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), - I_PV3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), - P_PV3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), - V_PV4(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), - I_PV4(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), - P_PV4(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), - PV_MODE(Doc.of(WorkMode.values())), // - TOTAL_INV_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - AC_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - AC_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE_REACTIVE).accessMode(AccessMode.READ_ONLY)), // - AC_APPARENT_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE).accessMode(AccessMode.READ_ONLY)), // - BACK_UP_V_LOAD_R(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), - BACK_UP_I_LOAD_R(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), - BACK_UP_F_LOAD_R(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_ONLY)), - LOAD_MODE_R(Doc.of(LoadMode.values())), // - BACK_UP_P_LOAD_R(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), - BACK_UP_V_LOAD_S(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), - BACK_UP_I_LOAD_S(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), - BACK_UP_F_LOAD_S(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_ONLY)), - LOAD_MODE_S(Doc.of(LoadMode.values())), // - BACK_UP_P_LOAD_S(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), - BACK_UP_V_LOAD_T(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), - BACK_UP_I_LOAD_T(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_ONLY)), - BACK_UP_F_LOAD_T(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_ONLY)), - LOAD_MODE_T(Doc.of(LoadMode.values())), // - BACK_UP_P_LOAD_T(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), - P_LOAD_R(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - P_LOAD_S(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - P_LOAD_T(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - TOTAL_BACK_UP_LOAD(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - TOTAL_LOAD_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - UPS_LOAD_PERCENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_ONLY)), // - AIR_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // - MODULE_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // - RADIATOR_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // - FUNCTION_BIT_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - BUS_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // - NBUS_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // - V_BATTERY1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // - I_BATTERY1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_ONLY)), // - P_BATTERY1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - BATTERY_MODE(Doc.of(BatteryMode.values())), // - WARNING_CODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - SAFETY_COUNTRY(Doc.of(SafetyCountry.values())), // . - WORK_MODE(Doc.of(WorkMode.values())), // - OPERATION_MODE(Doc.of(OperationMode.values())), // - - // Error Message - STATE_0(Doc.of(Level.FAULT).text("The GFCI detecting circuit is abnormal")), // - STATE_1(Doc.of(Level.FAULT).text("The output current sensor is abnormal")), // - STATE_2(Doc.of(Level.WARNING).text("TBD")), // - STATE_3(Doc.of(Level.FAULT).text("DCI Consistency Failure")), // - STATE_4(Doc.of(Level.FAULT).text("GFCI Consistency Failure")), // - STATE_5(Doc.of(Level.WARNING).text("TBD")), // - STATE_6(Doc.of(Level.FAULT).text("GFCI Device Failure")), // - STATE_7(Doc.of(Level.FAULT).text("Relay Device Failure")), // - STATE_8(Doc.of(Level.FAULT).text("AC HCT Failure")), // - STATE_9(Doc.of(Level.FAULT).text("Utility Loss")), // - STATE_10(Doc.of(Level.FAULT).text("Gournd I Failure")), // - STATE_11(Doc.of(Level.WARNING).text("DC Bus High")), // - STATE_12(Doc.of(Level.FAULT).text("Internal Fan Failure(Back-Up Over Load for ES)")), // - STATE_13(Doc.of(Level.WARNING).text("Over Temperature")), // - STATE_14(Doc.of(Level.FAULT).text("Auto Test Failure")), // - STATE_15(Doc.of(Level.WARNING).text("PV Over Voltage")), // - STATE_16(Doc.of(Level.FAULT).text("External Fan Failure")), // - STATE_17(Doc.of(Level.FAULT).text("Vac Failure")), // - STATE_18(Doc.of(Level.FAULT).text("Isolation Failure")), // - STATE_19(Doc.of(Level.WARNING).text("DC Injection High")), // - STATE_20(Doc.of(Level.WARNING).text("TBD")), // - STATE_21(Doc.of(Level.WARNING).text("TBD")), // - STATE_22(Doc.of(Level.FAULT).text("Fac Consistency Failure")), // - STATE_23(Doc.of(Level.FAULT).text("Vac Consistency Failure")), // - STATE_24(Doc.of(Level.WARNING).text("TBD")), // - STATE_25(Doc.of(Level.WARNING).text("Relay Check Failure")), // - STATE_26(Doc.of(Level.WARNING).text("TBD")), // - STATE_27(Doc.of(Level.WARNING).text("TBD")), // - STATE_28(Doc.of(Level.WARNING).text("TBD")), // - STATE_29(Doc.of(Level.FAULT).text("Fac Failure")), // - STATE_30(Doc.of(Level.FAULT).text("EEPROM R/W Failure")), // - STATE_31(Doc.of(Level.FAULT).text("Internal Communication Failure")), // - - PV_E_TOTAL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - PV_E_DAY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_TOTAL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - H_TOTAL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HOUR).accessMode(AccessMode.READ_ONLY)), // - E_DAY_SELL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_TOTAL_BUY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_DAY_BUY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_TOTAL_LOAD(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_LOAD_DAY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_BATTERY_CHARGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_CHARGE_DAY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_BATTERY_DISCHARGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_DISCHARGE_DAY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - BATT_STRINGS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - CPLD_WARNING_CODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - W_CHARGER_CTRL_FLAG(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - DERATE_FLAG(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - DERATE_FROZEN_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - DIAG_STATUS_H(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - DIAG_STATUS_L(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - - // External Communication Data (ARM) - COM_MODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - RSSI(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - MANIFACTURE_CODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - B_METER_COMMUNICATE_STATUS(Doc.of(MeterConnectStatus.values())), // - METER_COMMUNICATE_STATUS(Doc.of(MeterCommunicateStatus.values())), // - MT_ACTIVE_POWER_R(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - MT_ACTIVE_POWER_S(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - MT_ACTIVE_POWER_T(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - MT_TOTAL_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - MT_TOTAL_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_ONLY)), // - METER_PF_R(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - METER_PF_S(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - METER_PF_T(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - METER_POWER_FACTOR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - METER_FREQUENCE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - E_TOTAL_SELL(Doc.of(OpenemsType.FLOAT) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - E_TOTAL_BUY2(Doc.of(OpenemsType.FLOAT) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_ONLY)), // - - STATE_32(Doc.of(Level.WARNING).text("DRM0")), // - STATE_33(Doc.of(Level.WARNING).text("DRM1")), // - STATE_34(Doc.of(Level.WARNING).text("DRM2")), // - STATE_35(Doc.of(Level.WARNING).text("DRM3")), // - STATE_36(Doc.of(Level.WARNING).text("DRM4")), // - STATE_37(Doc.of(Level.WARNING).text("DRM5")), // - STATE_38(Doc.of(Level.WARNING).text("DRM6")), // - STATE_39(Doc.of(Level.WARNING).text("DRM7")), // - STATE_40(Doc.of(Level.WARNING).text("DRM8")), // - STATE_41(Doc.of(Level.WARNING).text("DRED Connect Status")), // - - BATTERY_TYPE_INDEX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - BMS_STATUS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - BMS_PACK_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_ONLY)), // - BMS_CHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - BMS_DISCHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - - STATE_42(Doc.of(Level.WARNING).text("Charging over-voltage2")), // - STATE_43(Doc.of(Level.WARNING).text("Discharging under-voltage2")), // - STATE_44(Doc.of(Level.WARNING).text("CellHigh temperature2")), // - STATE_45(Doc.of(Level.WARNING).text("CellLow temperature2")), // - STATE_46(Doc.of(Level.WARNING).text("Charging overcurrent2")), // - STATE_47(Doc.of(Level.WARNING).text("Discharging overcurrent2")), // - STATE_48(Doc.of(Level.WARNING).text("Precharge fault")), // - STATE_49(Doc.of(Level.WARNING).text("DC bus fault")), // - STATE_50(Doc.of(Level.WARNING).text("Battery break")), // - STATE_51(Doc.of(Level.WARNING).text("Battery Lock")), // - STATE_52(Doc.of(Level.WARNING).text("Discharge circuit Fault")), // - STATE_53(Doc.of(Level.WARNING).text("Charging circuit Failure")), // - STATE_54(Doc.of(Level.WARNING).text("Communication failure2")), // - STATE_55(Doc.of(Level.WARNING).text("Cell High temperature3")), // - STATE_56(Doc.of(Level.WARNING).text("Discharging under-voltage3")), // - STATE_57(Doc.of(Level.WARNING).text("Charging over-voltage3")), // - - BMS_SOH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_ONLY)), - BMS_BATTERY_STRINGS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - - STATE_58(Doc.of(Level.WARNING).text("Charging over-voltage1")), // - STATE_59(Doc.of(Level.WARNING).text("Discharging under-voltage1")), // - STATE_60(Doc.of(Level.WARNING).text("Cell High temperature1")), // - STATE_61(Doc.of(Level.WARNING).text("Cell Low temperature1")), // - STATE_62(Doc.of(Level.WARNING).text("Charging over-current1")), // - STATE_63(Doc.of(Level.WARNING).text("Discharging over-current1")), // - STATE_64(Doc.of(Level.WARNING).text("communication failure1")), // - STATE_65(Doc.of(Level.WARNING).text("System Reboot")), // - STATE_66(Doc.of(Level.WARNING).text("Cell- imbalance")), // - STATE_67(Doc.of(Level.WARNING).text("System Low temperature1")), // - STATE_68(Doc.of(Level.WARNING).text("System Low temperature2")), // - STATE_69(Doc.of(Level.WARNING).text("System High temperature")), // - - BATTERY_PROTOCOL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), // - - // Setting Parameter - USER_PASSWORD1(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - USER_PASSWORD2(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - USER_PASSWORD3(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - ROUTER_SSID(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - ROUTER_PASSWORD(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - ROUTER_ENCRYPTION_METHOD(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - DOMAIN1(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - PORT_NUMBER1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - DOMAIN2(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - PORT_NUMBER2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - MODBUS_ADDRESS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - MODBUS_MANUFACTURER(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - MODBUS_BADRATE_485(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - RTC_YEAR_MONTH_2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - RTC_DAY_HOUR_2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - RTC_MINUTE_SECOND_2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - SERIAL_NUMBER_2(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - DEVICE_TYPE_2(Doc.of(OpenemsType.STRING) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - RESUME_FACTORY_SETTING(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - CLEAR_DATA(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - START(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - STOP(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - RESET(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - RESET_SPS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - PV_E_TOTAL_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - PV_E_DAY_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_TOTAL_SELL_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - H_TOTAL_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HOUR).accessMode(AccessMode.READ_WRITE)), // - E_DAY_SELL_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_TOTAL_BUY_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_DAY_BUY_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_TOTAL_LOAD_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_LOAD_DAY_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_BATTERY_CHARGE_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_CHARGE_DAY_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_BATTERY_DISCHARGE_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - E_DISCHARGE_DAY_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.KILOWATT_HOURS).accessMode(AccessMode.READ_WRITE)), // - LANGUAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - SAFETY_COUNTRY_CODE(Doc.of(SafetyCountry.values())), // - ISO(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - LVRT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - ISLANDING(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BURN_IN_RESET_TIME(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MINUTE).accessMode(AccessMode.READ_WRITE)), // - PV_START_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - ENABLE_MPPT_4SHADOW(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BACK_UP_ENABLE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - AUTO_START_BACKUP(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - GRID_WAVE_CHECK_LEVEL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - REPAID_CUT_OFF(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BACKUP_START_DLY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - UPS_STD_VOLT_TYPE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - UNDER_ATS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BURN_IN_MODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BACKUP_OVERLOAD_DELAY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - UPSPHASE_TYPE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - DERATE_RATE_VDE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - LEAD_BAT_CAPACITY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATTERY_STRINGS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_CHARGE_VOLT_MAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_CHARGE_CURR_MAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_VOLT_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_DISCHARGE_CURR_MAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_SOC_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_OFF_LINE_VOLT_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_OFFLINE_SOC_UNDER_MIN(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - CLEAR_BATTERY_SETTING(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - - // CosPhi curve - ENABLE_CURVE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - POINT_A_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - POINT_A_PF(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - POINT_B_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - POINT_B_PF(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - POINT_C_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - POINT_C_PF(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - LOCK_IN_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - LOCK_OUT_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - LOCK_OUT_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - - // Power and frequency curve - STATE_70(Doc.of(Level.INFO).text("ON/OFF")), // - STATE_71(Doc.of(Level.INFO).text("response mode")), // - - FFROZEN_DCH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - FFROZEN_CH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - FSTOP_DCH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - FSTOP_CH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - RECOVERY_WAITING_TIME(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.SECONDS).accessMode(AccessMode.READ_WRITE)), // - RECOVERY_FREQURNCY1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - RECOVERY_FREQUENCY2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - RECOVERY_SLOPE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - FFROZEN_DCH_SLOPE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - FFROZEN_CH_SLOPE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HERTZ).accessMode(AccessMode.READ_WRITE)), // - DOWN_SLOPE_POWER_REFERENCE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - DOWN_SLOP(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - - // QU curve - ENABLE_CURVE_QU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - LOCK_IN_POWER_QU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - LOCK_OUT_POWER_QU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - V1_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V1_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - V2_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V2_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - V3_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V3_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - V4_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V4_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - K_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - TIME_CONSTANT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - MISCELLANEA(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - RATED_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - RESPONSE_TIME(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - - // PU curve - PU_CURVE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - POWER_CHANGE_RATE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - V1_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V1_VALUE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - V2_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V2_VALUE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - V3_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V3_VALUE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - V4_VOLTAGE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - V4_VALUE_PU(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - FIXED_POWER_FACTOR(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - FIXED_REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - FIXED_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - GRID_LIMIT_BY_VOLT_START_VOL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - GRID_LIMIT_BY_VOLT_START_PER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - GRID_LIMIT_BY_VOLT_SLOPE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.WRITE_ONLY)), // - AUTO_TEST_ENABLE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - AUTO_TEST_STEP(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - UW_ITALY_FREQ_MODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - - // Meter Control ARM - APP_MODE_INDEX(Doc.of(AppModeIndex.values())), // - METER_CHECK_VALUE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - WMETER_CONNECT_CHECK_FLAG(Doc.of(MeterConnectCheckFlag.values())), // - SIMULATE_METER_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BREEZE_ON_OFF(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - LOG_DATA_ENABLE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - DATA_SEND_INTERVAL(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.SECONDS).accessMode(AccessMode.READ_WRITE)), // - - // Battery Control Data ARM - STOP_SOC_PROTECT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATTERY_FLOAT_VOLT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - BATTERY_FLOAT_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_WRITE)), // - BATTERY_FLOAT_TIME(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MINUTE).accessMode(AccessMode.READ_WRITE)), // - BATTERY_TYPE_INDEX_ARM(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - MANUFACTURE_CODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - DC_VOLT_OUTPUT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BAT_AVG_CHG_VOLT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - BAT_AVG_CHG_HOURS(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.HOUR).accessMode(AccessMode.READ_WRITE)), // - FEED_POWER_ENABLE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - FEED_POWER_PARA(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT).accessMode(AccessMode.READ_WRITE)), // - EMS_POWER_MODE(Doc.of(PowerModeEms.values()) // - .accessMode(AccessMode.READ_WRITE)), // - EMS_POWER_SET(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BAT_BMS_CURR_LMT_COFF(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATTERY_PROTOCOL_ARM(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - START_TIME_1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - END_TIME_1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BAT_POWER_PERCENT_1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - - STATE_72(Doc.of(Level.INFO).text("SUNDAY")), // - STATE_73(Doc.of(Level.WARNING).text("MONDAY")), // - STATE_74(Doc.of(Level.WARNING).text("TUESDAY")), // - STATE_75(Doc.of(Level.WARNING).text("Wednesday")), // - STATE_76(Doc.of(Level.WARNING).text("Thursday")), // - STATE_77(Doc.of(Level.WARNING).text("Friday")), // - STATE_78(Doc.of(Level.WARNING).text("Saturday")), // - - START_TIME_2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - END_TIME_2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BAT_POWER_PERCENT_2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - START_TIME_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - END_TIME_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BAT_POWER_PERCENT_3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - START_TIME_4(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - END_TIME_4(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BAT_POWER_PERCENT_4(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - SOC_START_TO_FORCE_CHARGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - SOC_STOP_TO_FORCE_CHARGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - CLEAR_ALL_ECONOMIC_MODE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.WRITE_ONLY)), // - - // BMS for RS485 - BMS_VERSION(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - BATT_STRINGS_RS485(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.NONE).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_CHARGE_VMAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_CHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_DISCHARGE_VMIN(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_DISCHARGE_IMAX(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.AMPERE).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_SOC(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_SOH(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.PERCENT).accessMode(AccessMode.READ_WRITE)), // - WBMS_BAT_TEMPERATURE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.DEGREE_CELSIUS).accessMode(AccessMode.READ_WRITE)), // - - // BMS_STATUS(), // - STATE_79(Doc.of(Level.INFO).text("force to charge")), // - STATE_80(Doc.of(Level.INFO).text("Stop charging")), // TODO can be removed? - STATE_81(Doc.of(Level.INFO).text("Stop discharging")); - + ; private final Doc doc; private ChannelId(Doc doc) { @@ -704,22 +21,4 @@ public Doc doc() { } } - /** - * Gets the Channel for {@link ChannelId#GOODWE_TYPE}. - * - * @return the Channel - */ - public default Channel getGoodweTypeChannel() { - return this.channel(GoodWeEss.ChannelId.GOODWE_TYPE); - } - - /** - * Gets the Device Type. See {@link ChannelId#GOODWE_TYPE}. - * - * @return the Channel {@link Value} - */ - public default GoodweType getGoodweType() { - return this.getGoodweTypeChannel().value().asEnum(); - } - } diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEssImpl.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEssImpl.java index ebf1198f989..e048ddd729b 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEssImpl.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/ess/GoodWeEssImpl.java @@ -1,7 +1,6 @@ package io.openems.edge.goodwe.ess; -import java.util.HashSet; -import java.util.Set; +import java.util.Optional; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; @@ -17,32 +16,14 @@ import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; import org.osgi.service.metatype.annotations.Designate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.types.OpenemsType; -import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; -import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; -import io.openems.edge.bridge.modbus.api.ModbusProtocol; -import io.openems.edge.bridge.modbus.api.element.BitsWordElement; -import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; -import io.openems.edge.bridge.modbus.api.element.SignedWordElement; -import io.openems.edge.bridge.modbus.api.element.StringWordElement; -import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; -import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; -import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; -import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.common.channel.Channel; -import io.openems.edge.common.channel.EnumReadChannel; import io.openems.edge.common.channel.EnumWriteChannel; import io.openems.edge.common.channel.IntegerWriteChannel; 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.taskmanager.Priority; import io.openems.edge.common.type.TypeUtils; import io.openems.edge.ess.api.HybridEss; import io.openems.edge.ess.api.ManagedSymmetricEss; @@ -52,11 +33,10 @@ import io.openems.edge.ess.power.api.Power; import io.openems.edge.ess.power.api.Pwr; import io.openems.edge.ess.power.api.Relationship; -import io.openems.edge.goodwe.charger.AbstractGoodWeEtCharger; -import io.openems.edge.goodwe.ess.applypower.ApplyPowerStateMachine; -import io.openems.edge.goodwe.ess.applypower.Context; -import io.openems.edge.goodwe.ess.enums.BatteryMode; -import io.openems.edge.goodwe.ess.enums.GoodweType; +import io.openems.edge.goodwe.common.AbstractGoodWe; +import io.openems.edge.goodwe.common.GoodWe; +import io.openems.edge.goodwe.common.applypower.ApplyPowerStateMachine; +import io.openems.edge.goodwe.common.applypower.Context; import io.openems.edge.timedata.api.Timedata; import io.openems.edge.timedata.api.TimedataProvider; import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; @@ -68,11 +48,9 @@ configurationPolicy = ConfigurationPolicy.REQUIRE, // property = EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE // ) // -public class GoodWeEssImpl extends AbstractOpenemsModbusComponent implements GoodWeEss, HybridEss, ManagedSymmetricEss, +public class GoodWeEssImpl extends AbstractGoodWe implements GoodWeEss, GoodWe, HybridEss, ManagedSymmetricEss, SymmetricEss, OpenemsComponent, TimedataProvider, EventHandler { - private final Logger log = LoggerFactory.getLogger(GoodWeEssImpl.class); - private Config config; @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) @@ -89,8 +67,6 @@ public class GoodWeEssImpl extends AbstractOpenemsModbusComponent implements Goo private final CalculateEnergyFromPower calculateAcDischargeEnergy = new CalculateEnergyFromPower(this, SymmetricEss.ChannelId.ACTIVE_DISCHARGE_ENERGY); - private final Set chargers = new HashSet<>(); - private final ApplyPowerStateMachine applyPowerStateMachine = new ApplyPowerStateMachine( ApplyPowerStateMachine.State.UNDEFINED); @@ -120,337 +96,20 @@ public GoodWeEssImpl() throws OpenemsNamedException { SymmetricEss.ChannelId.values(), // ManagedSymmetricEss.ChannelId.values(), // HybridEss.ChannelId.values(), // + GoodWe.ChannelId.values(), // GoodWeEss.ChannelId.values() // ); } - @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { - return new ModbusProtocol(this, // - - new FC3ReadRegistersTask(35001, Priority.LOW, // - m(SymmetricEss.ChannelId.MAX_APPARENT_POWER, new UnsignedWordElement(35001)), // - new DummyRegisterElement(35002), // - m(GoodWeEss.ChannelId.SERIAL_NUMBER, new StringWordElement(35003, 8)), // - m(GoodWeEss.ChannelId.GOODWE_TYPE, new StringWordElement(35011, 5), - new ElementToChannelConverter( - // element -> channel - value -> { - // Evaluate GoodweType - final GoodweType result; - if (value == null) { - result = GoodweType.UNDEFINED; - } else { - String stringValue = TypeUtils.getAsType(OpenemsType.STRING, - value); - switch (stringValue) { - case "GW10K-BT": - result = GoodweType.GOODWE_10K_BT; - break; - case "GW10K-ET": - result = GoodweType.GOODWE_10K_ET; - break; - default: - result = GoodweType.UNDEFINED; - break; - } - } - // Log on first occurrence - if (result != this.getGoodweType()) { - switch (result) { - case GOODWE_10K_BT: - this.logInfo(this.log, "Identified GoodWe GW10K-BT"); - break; - case GOODWE_10K_ET: - this.logInfo(this.log, "Identified GoodWe GW10K-ET"); - break; - case UNDEFINED: - break; - } - } - return result; - }, // - // channel -> element - value -> value))), - - new FC3ReadRegistersTask(35111, Priority.LOW, // - m(GoodWeEss.ChannelId.V_PV3, new UnsignedWordElement(35111), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.I_PV3, new UnsignedWordElement(35112), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - new DummyRegisterElement(35113, 35114), // - m(GoodWeEss.ChannelId.V_PV4, new UnsignedWordElement(35115), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.I_PV4, new UnsignedWordElement(35116), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - new DummyRegisterElement(35117, 35118), // - m(GoodWeEss.ChannelId.PV_MODE, new UnsignedDoublewordElement(35119))), // - - new FC3ReadRegistersTask(35136, Priority.LOW, // - m(SymmetricEss.ChannelId.GRID_MODE, new UnsignedWordElement(35136), // - new ElementToChannelConverter((value) -> { - Integer intValue = TypeUtils.getAsType(OpenemsType.INTEGER, value); - if (intValue != null) { - switch (intValue) { - case 0: - return GridMode.OFF_GRID; - case 1: - return GridMode.ON_GRID; - case 2: - return GridMode.UNDEFINED; - } - } - return GridMode.UNDEFINED; - }))), // - new FC3ReadRegistersTask(35138, Priority.LOW, // - m(GoodWeEss.ChannelId.TOTAL_INV_POWER, new SignedWordElement(35138)), // - new DummyRegisterElement(35139), // - m(GoodWeEss.ChannelId.AC_ACTIVE_POWER, new SignedWordElement(35140), // - ElementToChannelConverter.INVERT), // - new DummyRegisterElement(35141), // - m(GoodWeEss.ChannelId.AC_REACTIVE_POWER, new SignedWordElement(35142), // - ElementToChannelConverter.INVERT), // - new DummyRegisterElement(35143), // - m(GoodWeEss.ChannelId.AC_APPARENT_POWER, new SignedWordElement(35144), // - ElementToChannelConverter.INVERT), // - m(GoodWeEss.ChannelId.BACK_UP_V_LOAD_R, new UnsignedWordElement(35145), // - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.BACK_UP_I_LOAD_R, new UnsignedWordElement(35146), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.BACK_UP_F_LOAD_R, new UnsignedWordElement(35147), - ElementToChannelConverter.SCALE_FACTOR_MINUS_2), // - m(GoodWeEss.ChannelId.LOAD_MODE_R, new UnsignedWordElement(35148)), // - new DummyRegisterElement(35149), // - m(GoodWeEss.ChannelId.BACK_UP_P_LOAD_R, new SignedWordElement(35150)), // - m(GoodWeEss.ChannelId.BACK_UP_V_LOAD_S, new UnsignedWordElement(35151), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.BACK_UP_I_LOAD_S, new UnsignedWordElement(35152), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.BACK_UP_F_LOAD_S, new UnsignedWordElement(35153), - ElementToChannelConverter.SCALE_FACTOR_MINUS_2), // - m(GoodWeEss.ChannelId.LOAD_MODE_S, new UnsignedWordElement(35154)), // - new DummyRegisterElement(35155), // - m(GoodWeEss.ChannelId.BACK_UP_P_LOAD_S, new SignedWordElement(35156)), // - m(GoodWeEss.ChannelId.BACK_UP_V_LOAD_T, new UnsignedWordElement(35157), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.BACK_UP_I_LOAD_T, new UnsignedWordElement(35158), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.BACK_UP_F_LOAD_T, new UnsignedWordElement(35159), - ElementToChannelConverter.SCALE_FACTOR_MINUS_2), // - m(GoodWeEss.ChannelId.LOAD_MODE_T, new UnsignedWordElement(35160)), // - new DummyRegisterElement(35161), // - m(GoodWeEss.ChannelId.BACK_UP_P_LOAD_T, new SignedWordElement(35162)), // - new DummyRegisterElement(35163), // - m(GoodWeEss.ChannelId.P_LOAD_R, new SignedWordElement(35164)), // - new DummyRegisterElement(35165), // - m(GoodWeEss.ChannelId.P_LOAD_S, new SignedWordElement(35166)), // - new DummyRegisterElement(35167), // - m(GoodWeEss.ChannelId.P_LOAD_T, new SignedWordElement(35168)), // - new DummyRegisterElement(35169), // - m(GoodWeEss.ChannelId.TOTAL_BACK_UP_LOAD, new SignedWordElement(35170)), // - new DummyRegisterElement(35171), // - m(GoodWeEss.ChannelId.TOTAL_LOAD_POWER, new SignedWordElement(35172)), // - m(GoodWeEss.ChannelId.UPS_LOAD_PERCENT, new UnsignedWordElement(35173), - ElementToChannelConverter.SCALE_FACTOR_MINUS_2)), // - - new FC3ReadRegistersTask(35180, Priority.HIGH, // - m(GoodWeEss.ChannelId.V_BATTERY1, new UnsignedWordElement(35180), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.I_BATTERY1, new SignedWordElement(35181), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - new DummyRegisterElement(35182), // - // required for calculation of ActivePower - m(GoodWeEss.ChannelId.P_BATTERY1, new SignedWordElement(35183)), - m(GoodWeEss.ChannelId.BATTERY_MODE, new UnsignedWordElement(35184))), // - - new FC3ReadRegistersTask(35185, Priority.LOW, // - m(GoodWeEss.ChannelId.WARNING_CODE, new UnsignedWordElement(35185)), // - m(GoodWeEss.ChannelId.SAFETY_COUNTRY, new UnsignedWordElement(35186)), // - m(GoodWeEss.ChannelId.WORK_MODE, new UnsignedWordElement(35187)), // - m(GoodWeEss.ChannelId.OPERATION_MODE, new UnsignedDoublewordElement(35188))), // - - new FC3ReadRegistersTask(35206, Priority.LOW, // - m(HybridEss.ChannelId.DC_CHARGE_ENERGY, new UnsignedDoublewordElement(35206), // - ElementToChannelConverter.SCALE_FACTOR_2), // - new DummyRegisterElement(35208), // - m(HybridEss.ChannelId.DC_DISCHARGE_ENERGY, new UnsignedDoublewordElement(35209), - ElementToChannelConverter.SCALE_FACTOR_2)), // - - new FC3ReadRegistersTask(36003, Priority.LOW, // - m(GoodWeEss.ChannelId.B_METER_COMMUNICATE_STATUS, new UnsignedWordElement(36003)), // - m(GoodWeEss.ChannelId.METER_COMMUNICATE_STATUS, new UnsignedWordElement(36004))), // - - new FC3ReadRegistersTask(37001, Priority.LOW, - m(GoodWeEss.ChannelId.BATTERY_TYPE_INDEX, new UnsignedWordElement(37001)), // - m(GoodWeEss.ChannelId.BMS_STATUS, new UnsignedWordElement(37002)), // - m(GoodWeEss.ChannelId.BMS_PACK_TEMPERATURE, new UnsignedWordElement(37003), - ElementToChannelConverter.SCALE_FACTOR_MINUS_1), // - m(GoodWeEss.ChannelId.BMS_CHARGE_IMAX, new UnsignedWordElement(37004)), // - m(GoodWeEss.ChannelId.BMS_DISCHARGE_IMAX, new UnsignedWordElement(37005))), // - - new FC3ReadRegistersTask(37007, Priority.LOW, // - m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(37007), new ElementToChannelConverter( - // element -> channel - value -> { - // Set SoC to undefined if there is No Battery - EnumReadChannel batteryModeChannel = this.channel(GoodWeEss.ChannelId.BATTERY_MODE); - BatteryMode batteryMode = batteryModeChannel.value().asEnum(); - if (batteryMode == BatteryMode.NO_BATTERY || batteryMode == BatteryMode.UNDEFINED) { - return null; - } else { - return value; - } - }, - // channel -> element - value -> value))), // - - new FC3ReadRegistersTask(37008, Priority.LOW, // - m(GoodWeEss.ChannelId.BMS_SOH, new UnsignedWordElement(37008)), // - m(GoodWeEss.ChannelId.BMS_BATTERY_STRINGS, new UnsignedWordElement(37009))), // - - new FC16WriteRegistersTask(47000, // - m(GoodWeEss.ChannelId.APP_MODE_INDEX, new UnsignedWordElement(47000)), // - m(GoodWeEss.ChannelId.METER_CHECK_VALUE, new UnsignedWordElement(47001)), // - m(GoodWeEss.ChannelId.WMETER_CONNECT_CHECK_FLAG, new UnsignedWordElement(47002))), // - - new FC3ReadRegistersTask(47000, Priority.LOW, // - m(GoodWeEss.ChannelId.APP_MODE_INDEX, new UnsignedWordElement(47000)), // - m(GoodWeEss.ChannelId.METER_CHECK_VALUE, new UnsignedWordElement(47001)), // - m(GoodWeEss.ChannelId.WMETER_CONNECT_CHECK_FLAG, new UnsignedWordElement(47002))), // - - new FC16WriteRegistersTask(47500, // - m(GoodWeEss.ChannelId.STOP_SOC_PROTECT, new UnsignedWordElement(47500)), // - new DummyRegisterElement(47501, 47508), // - m(GoodWeEss.ChannelId.FEED_POWER_ENABLE, new UnsignedWordElement(47509)), // - m(GoodWeEss.ChannelId.FEED_POWER_PARA, new UnsignedWordElement(47510)), // - m(GoodWeEss.ChannelId.EMS_POWER_MODE, new UnsignedWordElement(47511)), // - m(GoodWeEss.ChannelId.EMS_POWER_SET, new UnsignedWordElement(47512))), // - - new FC16WriteRegistersTask(47531, // - m(GoodWeEss.ChannelId.SOC_START_TO_FORCE_CHARGE, new UnsignedWordElement(47531)), // - m(GoodWeEss.ChannelId.SOC_STOP_TO_FORCE_CHARGE, new UnsignedWordElement(47532)), // - m(GoodWeEss.ChannelId.CLEAR_ALL_ECONOMIC_MODE, new UnsignedWordElement(47533))), // - - new FC3ReadRegistersTask(47500, Priority.LOW, - m(GoodWeEss.ChannelId.STOP_SOC_PROTECT, new UnsignedWordElement(47500)), // - new DummyRegisterElement(47501, 47508), // - m(GoodWeEss.ChannelId.FEED_POWER_ENABLE, new UnsignedWordElement(47509)), // - m(GoodWeEss.ChannelId.FEED_POWER_PARA, new UnsignedWordElement(47510))), // - - new FC3ReadRegistersTask(47511, Priority.LOW, - m(GoodWeEss.ChannelId.EMS_POWER_MODE, new UnsignedWordElement(47511)), // - m(GoodWeEss.ChannelId.EMS_POWER_SET, new UnsignedWordElement(47512))), // - - new FC3ReadRegistersTask(47531, Priority.LOW, - m(GoodWeEss.ChannelId.SOC_START_TO_FORCE_CHARGE, new UnsignedWordElement(47531)), // - m(GoodWeEss.ChannelId.SOC_STOP_TO_FORCE_CHARGE, new UnsignedWordElement(47532))), // - - new FC16WriteRegistersTask(47900, // - m(GoodWeEss.ChannelId.BMS_VERSION, new UnsignedWordElement(47900)), // - m(GoodWeEss.ChannelId.BATT_STRINGS_RS485, new UnsignedWordElement(47901)), // - m(GoodWeEss.ChannelId.WBMS_BAT_CHARGE_VMAX, new UnsignedWordElement(47902)), // - m(GoodWeEss.ChannelId.WBMS_BAT_CHARGE_IMAX, new UnsignedWordElement(47903)), // - m(GoodWeEss.ChannelId.WBMS_BAT_DISCHARGE_VMIN, new UnsignedWordElement(47904)), // - m(GoodWeEss.ChannelId.WBMS_BAT_DISCHARGE_IMAX, new UnsignedWordElement(47905)), // - m(GoodWeEss.ChannelId.WBMS_BAT_VOLTAGE, new UnsignedWordElement(47906)), // - m(GoodWeEss.ChannelId.WBMS_BAT_CURRENT, new UnsignedWordElement(47907)), // - m(GoodWeEss.ChannelId.WBMS_BAT_SOC, new UnsignedWordElement(47908)), // - m(GoodWeEss.ChannelId.WBMS_BAT_SOH, new UnsignedWordElement(47909)), // - m(GoodWeEss.ChannelId.WBMS_BAT_TEMPERATURE, new UnsignedWordElement(47910)), // - m(new BitsWordElement(47911, this) // - .bit(0, GoodWeEss.ChannelId.STATE_58) // - .bit(1, GoodWeEss.ChannelId.STATE_59) // - .bit(2, GoodWeEss.ChannelId.STATE_60) // - .bit(3, GoodWeEss.ChannelId.STATE_61) // - .bit(4, GoodWeEss.ChannelId.STATE_62) // - .bit(5, GoodWeEss.ChannelId.STATE_63) // - .bit(6, GoodWeEss.ChannelId.STATE_64) // - .bit(7, GoodWeEss.ChannelId.STATE_65) // - .bit(8, GoodWeEss.ChannelId.STATE_66) // - .bit(9, GoodWeEss.ChannelId.STATE_67) // - .bit(10, GoodWeEss.ChannelId.STATE_68) // - .bit(11, GoodWeEss.ChannelId.STATE_69)), // - new DummyRegisterElement(47912), // - m(new BitsWordElement(47913, this) // - .bit(0, GoodWeEss.ChannelId.STATE_42) // - .bit(1, GoodWeEss.ChannelId.STATE_43) // - .bit(2, GoodWeEss.ChannelId.STATE_44) // - .bit(3, GoodWeEss.ChannelId.STATE_45) // - .bit(4, GoodWeEss.ChannelId.STATE_46) // - .bit(5, GoodWeEss.ChannelId.STATE_47) // - .bit(6, GoodWeEss.ChannelId.STATE_48) // - .bit(7, GoodWeEss.ChannelId.STATE_49) // - .bit(8, GoodWeEss.ChannelId.STATE_50) // - .bit(9, GoodWeEss.ChannelId.STATE_51) // - .bit(10, GoodWeEss.ChannelId.STATE_52) // - .bit(11, GoodWeEss.ChannelId.STATE_53) // - .bit(12, GoodWeEss.ChannelId.STATE_54) // - .bit(13, GoodWeEss.ChannelId.STATE_55) // - .bit(14, GoodWeEss.ChannelId.STATE_56) // - .bit(15, GoodWeEss.ChannelId.STATE_57)), // - new DummyRegisterElement(47914), // - m(new BitsWordElement(47915, this) // - .bit(0, GoodWeEss.ChannelId.STATE_79) // - .bit(1, GoodWeEss.ChannelId.STATE_80) // - .bit(2, GoodWeEss.ChannelId.STATE_81))), // - - new FC3ReadRegistersTask(47902, Priority.LOW, // - m(GoodWeEss.ChannelId.WBMS_BAT_CHARGE_VMAX, new UnsignedWordElement(47902)), // - m(GoodWeEss.ChannelId.WBMS_BAT_CHARGE_IMAX, new UnsignedWordElement(47903)), // - m(GoodWeEss.ChannelId.WBMS_BAT_DISCHARGE_VMIN, new UnsignedWordElement(47904)), // - m(GoodWeEss.ChannelId.WBMS_BAT_DISCHARGE_IMAX, new UnsignedWordElement(47905)), // - m(GoodWeEss.ChannelId.WBMS_BAT_VOLTAGE, new UnsignedWordElement(47906)), // - m(GoodWeEss.ChannelId.WBMS_BAT_CURRENT, new UnsignedWordElement(47907)), // - m(GoodWeEss.ChannelId.WBMS_BAT_SOC, new UnsignedWordElement(47908)), // - m(GoodWeEss.ChannelId.WBMS_BAT_SOH, new UnsignedWordElement(47909)), // - m(GoodWeEss.ChannelId.WBMS_BAT_TEMPERATURE, new UnsignedWordElement(47910)), // - m(new BitsWordElement(47911, this) // - .bit(0, GoodWeEss.ChannelId.STATE_58) // - .bit(1, GoodWeEss.ChannelId.STATE_59) // - .bit(2, GoodWeEss.ChannelId.STATE_60) // - .bit(3, GoodWeEss.ChannelId.STATE_61) // - .bit(4, GoodWeEss.ChannelId.STATE_62) // - .bit(5, GoodWeEss.ChannelId.STATE_63) // - .bit(6, GoodWeEss.ChannelId.STATE_64) // - .bit(7, GoodWeEss.ChannelId.STATE_65) // - .bit(8, GoodWeEss.ChannelId.STATE_66) // - .bit(9, GoodWeEss.ChannelId.STATE_67) // - .bit(10, GoodWeEss.ChannelId.STATE_68) // - .bit(11, GoodWeEss.ChannelId.STATE_69)), // - new DummyRegisterElement(47912), // - m(new BitsWordElement(47913, this) // - .bit(0, GoodWeEss.ChannelId.STATE_42) // - .bit(1, GoodWeEss.ChannelId.STATE_43) // - .bit(2, GoodWeEss.ChannelId.STATE_44) // - .bit(3, GoodWeEss.ChannelId.STATE_45) // - .bit(4, GoodWeEss.ChannelId.STATE_46) // - .bit(5, GoodWeEss.ChannelId.STATE_47) // - .bit(6, GoodWeEss.ChannelId.STATE_48) // - .bit(7, GoodWeEss.ChannelId.STATE_49) // - .bit(8, GoodWeEss.ChannelId.STATE_50) // - .bit(9, GoodWeEss.ChannelId.STATE_51) // - .bit(10, GoodWeEss.ChannelId.STATE_52) // - .bit(11, GoodWeEss.ChannelId.STATE_53) // - .bit(12, GoodWeEss.ChannelId.STATE_54) // - .bit(13, GoodWeEss.ChannelId.STATE_55) // - .bit(14, GoodWeEss.ChannelId.STATE_56) // - .bit(15, GoodWeEss.ChannelId.STATE_57)), // - new DummyRegisterElement(47914), // - m(new BitsWordElement(47915, this) // - .bit(0, GoodWeEss.ChannelId.STATE_79) // - .bit(1, GoodWeEss.ChannelId.STATE_80) // - .bit(2, GoodWeEss.ChannelId.STATE_81)))); - } - @Override public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException { - int pvProduction = getPvProduction(); + int pvProduction = Optional.ofNullable(this.calculatePvProduction()).orElse(0); int soc = this.getSoc().orElse(0); ApplyPowerStateMachine.State state = ApplyPowerStateMachine.evaluateState(this.getGoodweType(), - config.readOnlyMode(), pvProduction, soc, activePower); + this.config.readOnlyMode(), pvProduction, soc, activePower); // Store the current State - this.channel(GoodWeEss.ChannelId.APPLY_POWER_STATE_MACHINE).setNextValue(state); + this.channel(GoodWe.ChannelId.APPLY_POWER_STATE_MACHINE).setNextValue(state); // Prepare Context Context context = new Context(this, pvProduction, activePower); @@ -459,11 +118,12 @@ public void applyPower(int activePower, int reactivePower) throws OpenemsNamedEx this.applyPowerStateMachine.run(context); // apply the force next state this.applyPowerStateMachine.run(context); // execute correct handler - IntegerWriteChannel emsPowerSetChannel = this.channel(GoodWeEss.ChannelId.EMS_POWER_SET); + IntegerWriteChannel emsPowerSetChannel = this.channel(GoodWe.ChannelId.EMS_POWER_SET); emsPowerSetChannel.setNextWriteValue(context.getEssPowerSet()); - EnumWriteChannel emsPowerModeChannel = this.channel(GoodWeEss.ChannelId.EMS_POWER_MODE); + EnumWriteChannel emsPowerModeChannel = this.channel(GoodWe.ChannelId.EMS_POWER_MODE); emsPowerModeChannel.setNextWriteValue(context.getNextPowerMode()); + } @Override @@ -475,16 +135,6 @@ public String debugLog() { + this.getAllowedDischargePower().asString(); } - @Override - public void addCharger(AbstractGoodWeEtCharger charger) { - this.chargers.add(charger); - } - - @Override - public void removeCharger(AbstractGoodWeEtCharger charger) { - this.chargers.remove(charger); - } - @Override public void handleEvent(Event event) { if (!this.isEnabled()) { @@ -502,11 +152,8 @@ private void updatechannels() { /* * Update ActivePower from P_BATTERY1 and chargers ACTUAL_POWER */ - final Channel batteryPower = this.channel(GoodWeEss.ChannelId.P_BATTERY1); - Integer productionPower = null; - for (AbstractGoodWeEtCharger charger : this.chargers) { - productionPower = TypeUtils.sum(productionPower, charger.getActualPower().get()); - } + Integer productionPower = this.calculatePvProduction(); + final Channel batteryPower = this.channel(GoodWe.ChannelId.P_BATTERY1); Integer activePower = TypeUtils.sum(productionPower, batteryPower.value().get()); this._setActivePower(activePower); @@ -605,28 +252,11 @@ public Integer getSurplusPower() { if (this.getSoc().orElse(0) < 99) { return null; } - Integer productionPower = null; - for (AbstractGoodWeEtCharger charger : this.chargers) { - productionPower = TypeUtils.sum(productionPower, charger.getActualPower().get()); - } + Integer productionPower = this.calculatePvProduction(); if (productionPower == null || productionPower < 100) { return null; } return productionPower; } - /** - * Gets the PV production. Returns 0 if the PV production is not available. - * - * @return production power - */ - private int getPvProduction() { - int productionPower = 0; - for (AbstractGoodWeEtCharger charger : this.chargers) { - productionPower = TypeUtils.sum(productionPower, charger.getActualPower().get()); - } - - return productionPower; - } - } diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/Config.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/Config.java index 104bc944786..7806737472c 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/Config.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/Config.java @@ -3,9 +3,11 @@ import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.edge.goodwe.GoodWeConstants; + @ObjectClassDefinition(// - name = "GoodWe ET Grid-Meter", // - description = "The grid-meter implementation of a Goodwe.") + name = "GoodWe Grid-Meter", // + description = "GoodWe Smart Meter.") @interface Config { @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") @@ -17,15 +19,15 @@ @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") boolean enabled() default true; - @AttributeDefinition(name = "Modbus Unit-id", description = "Unit-id") - int unit_id() default 0xF7; - @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.") String modbus_id() default "modbus0"; + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") + int modbusUnitId() default GoodWeConstants.DEFAULT_UNIT_ID; + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "GoodWe ET Grid-Meter [{id}]"; + String webconsole_configurationFactory_nameHint() default "GoodWe Grid-Meter [{id}]"; -} \ No newline at end of file +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeter.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeter.java index 0527294eb42..43bc4423cf4 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeter.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeter.java @@ -73,8 +73,8 @@ public GoodWeGridMeter() { @Activate void activate(ComponentContext context, Config config) throws OpenemsException { - if (super.activate(context, config.id(), config.alias(), config.enabled(), config.unit_id(), this.cm, "Modbus", - config.modbus_id())) { + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id())) { return; } } diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImplTest.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImplTest.java new file mode 100644 index 00000000000..f108c9c727e --- /dev/null +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImplTest.java @@ -0,0 +1,48 @@ +package io.openems.edge.goodwe.batteryinverter; + +import org.junit.Test; + +import io.openems.edge.bridge.modbus.test.DummyModbusBridge; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyConfigurationAdmin; +import io.openems.edge.ess.test.DummyPower; +import io.openems.edge.goodwe.GoodWeConstants; +import io.openems.edge.goodwe.charger.GoodWeChargerPv1; + +public class GoodWeBatteryInverterImplTest { + + private static final String MODBUS_ID = "modbus0"; + + private static final String BATTERY_INVERTER_ID = "batteryInverter0"; + + private static final String CHARGER_ID = "charger0"; + + @Test + public void testEt() throws Exception { + GoodWeChargerPv1 charger = new GoodWeChargerPv1(); + new ComponentTest(charger) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // + .activate(io.openems.edge.goodwe.charger.MyConfig.create() // + .setId(CHARGER_ID) // + .setBatteryInverterId(BATTERY_INVERTER_ID) // + .setModbusId(MODBUS_ID) // + .setModbusUnitId(GoodWeConstants.DEFAULT_UNIT_ID) // + .build()); + + GoodWeBatteryInverterImpl ess = new GoodWeBatteryInverterImpl(); + ess.addCharger(charger); + new ComponentTest(ess) // + .addReference("power", new DummyPower()) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // + .addComponent(charger) // + .activate(MyConfig.create() // + .setId(BATTERY_INVERTER_ID) // + .setModbusId(MODBUS_ID) // + .setModbusUnitId(GoodWeConstants.DEFAULT_UNIT_ID) // + .build()) // + ; + } + +} diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/MyConfig.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/MyConfig.java new file mode 100644 index 00000000000..1ba4b14ea29 --- /dev/null +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/MyConfig.java @@ -0,0 +1,68 @@ +package io.openems.edge.goodwe.batteryinverter; + +import io.openems.common.utils.ConfigUtils; +import io.openems.edge.common.test.AbstractComponentConfig; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + protected static class Builder { + private String id = null; + public String modbusId; + public int modbusUnitId; + + private Builder() { + + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setModbusId(String modbusId) { + this.modbusId = modbusId; + return this; + } + + public Builder setModbusUnitId(int modbusUnitId) { + this.modbusUnitId = modbusUnitId; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public int modbusUnitId() { + return this.builder.modbusUnitId; + } + + @Override + public String modbus_id() { + return this.builder.modbusId; + } + + @Override + public String Modbus_target() { + return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id()); + } +} \ No newline at end of file diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv1Test.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv1Test.java index a78d6f9dd2b..107afa69ad8 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv1Test.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv1Test.java @@ -19,7 +19,7 @@ public void test() throws Exception { .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // .activate(MyConfig.create() // .setId(CHARGER_ID) // - .setEssId(ESS_ID) // + .setBatteryInverterId(ESS_ID) // .setModbusId(MODBUS_ID) // .build()); } diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv2Test.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv2Test.java index 1f889975748..ca32d10a7f3 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv2Test.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/GoodWeChargerPv2Test.java @@ -19,7 +19,7 @@ public void test() throws Exception { .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // .activate(MyConfig.create() // .setId(CHARGER_ID) // - .setEssId(ESS_ID) // + .setBatteryInverterId(ESS_ID) // .setModbusId(MODBUS_ID) // .build()); } diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/MyConfig.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/MyConfig.java index 10576652bd6..7d54857a00d 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/MyConfig.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/MyConfig.java @@ -8,9 +8,9 @@ public class MyConfig extends AbstractComponentConfig implements ConfigPV1, Conf public static class Builder { private String id = null; - public String essId; - public int unitId; + public String essOrBatteryInverter; public String modbusId; + public int modbusUnitId; private Builder() { @@ -21,8 +21,8 @@ public Builder setId(String id) { return this; } - public Builder setEssId(String essId) { - this.essId = essId; + public Builder setBatteryInverterId(String essOrBatteryInverter) { + this.essOrBatteryInverter = essOrBatteryInverter; return this; } @@ -31,11 +31,16 @@ public Builder setModbusId(String modbusId) { return this; } - public Builder setUnitId(int unitId) { - this.unitId = unitId; + public Builder setModbusUnitId(int modbusUnitId) { + this.modbusUnitId = modbusUnitId; return this; } + /** + * Builds the Config. + * + * @return the Config + */ public MyConfig build() { return new MyConfig(this); } @@ -58,8 +63,8 @@ private MyConfig(Builder builder) { } @Override - public int unit_id() { - return this.builder.unitId; + public int modbusUnitId() { + return this.builder.modbusUnitId; } @Override @@ -73,13 +78,13 @@ public String Modbus_target() { } @Override - public String ess_id() { - return this.builder.essId; + public String essOrBatteryInverter_id() { + return this.builder.essOrBatteryInverter; } @Override - public String Ess_target() { - return ConfigUtils.generateReferenceTargetFilter(this.id(), this.ess_id()); + public String essOrBatteryInverter_target() { + return ConfigUtils.generateReferenceTargetFilter(this.id(), this.essOrBatteryInverter_id()); } -} \ No newline at end of file +} diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/GoodWeEtBatteryInverterImplTest.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/GoodWeEssImplTest.java similarity index 95% rename from io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/GoodWeEtBatteryInverterImplTest.java rename to io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/GoodWeEssImplTest.java index 389aa184c5a..6a18bec8aaa 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/GoodWeEtBatteryInverterImplTest.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/GoodWeEssImplTest.java @@ -11,10 +11,10 @@ import io.openems.edge.ess.test.ManagedSymmetricEssTest; import io.openems.edge.goodwe.GoodWeConstants; import io.openems.edge.goodwe.charger.GoodWeChargerPv1; -import io.openems.edge.goodwe.ess.enums.GoodweType; -import io.openems.edge.goodwe.ess.enums.PowerModeEms; +import io.openems.edge.goodwe.common.enums.GoodweType; +import io.openems.edge.goodwe.common.enums.PowerModeEms; -public class GoodWeEtBatteryInverterImplTest { +public class GoodWeEssImplTest { private static final String MODBUS_ID = "modbus0"; @@ -37,9 +37,9 @@ public void testEt() throws Exception { .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // .activate(io.openems.edge.goodwe.charger.MyConfig.create() // .setId(CHARGER_ID) // - .setEssId(ESS_ID) // + .setBatteryInverterId(ESS_ID) // .setModbusId(MODBUS_ID) // - .setUnitId(GoodWeConstants.DEFAULT_UNIT_ID) // + .setModbusUnitId(GoodWeConstants.DEFAULT_UNIT_ID) // .build()); GoodWeEssImpl ess = new GoodWeEssImpl(); diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/MyConfig.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/MyConfig.java index c186eefdd75..4c5bd1bcab4 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/MyConfig.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/ess/MyConfig.java @@ -2,7 +2,6 @@ import io.openems.common.utils.ConfigUtils; import io.openems.edge.common.test.AbstractComponentConfig; -import io.openems.edge.goodwe.ess.Config; @SuppressWarnings("all") public class MyConfig extends AbstractComponentConfig implements Config { @@ -100,4 +99,4 @@ public String Modbus_target() { return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id()); } -} \ No newline at end of file +} diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/gridmeter/MyConfig.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/gridmeter/MyConfig.java index 1f6fb74baf4..d0c920e9023 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/gridmeter/MyConfig.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/gridmeter/MyConfig.java @@ -2,36 +2,35 @@ import io.openems.common.utils.ConfigUtils; import io.openems.edge.common.test.AbstractComponentConfig; -import io.openems.edge.goodwe.charger.ConfigPV1; @SuppressWarnings("all") public class MyConfig extends AbstractComponentConfig implements Config { public static class Builder { private String id = null; - public int unitId; + public int modbusUnitId; public String modbusId; private Builder() { } - public Builder setId(String id) { + protected Builder setId(String id) { this.id = id; return this; } - public Builder setModbusId(String modbusId) { + protected Builder setModbusId(String modbusId) { this.modbusId = modbusId; return this; } - public Builder setUnitId(int unitId) { - this.unitId = unitId; + protected Builder setModbusUnitId(int modbusUnitId) { + this.modbusUnitId = modbusUnitId; return this; } - public MyConfig build() { + protected MyConfig build() { return new MyConfig(this); } } @@ -48,13 +47,13 @@ public static Builder create() { private final Builder builder; private MyConfig(Builder builder) { - super(ConfigPV1.class, builder.id); + super(Config.class, builder.id); this.builder = builder; } @Override - public int unit_id() { - return this.builder.unitId; + public int modbusUnitId() { + return this.builder.modbusUnitId; } @Override diff --git a/io.openems.edge.io.kmtronic/.settings/org.eclipse.core.resources.prefs b/io.openems.edge.io.kmtronic/.settings/org.eclipse.core.resources.prefs index da1771d999a..b91a8264c3c 100644 --- a/io.openems.edge.io.kmtronic/.settings/org.eclipse.core.resources.prefs +++ b/io.openems.edge.io.kmtronic/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,5 @@ eclipse.preferences.version=1 -encoding//src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java=UTF-8 +encoding//src/io/openems/edge/io/kmtronic/AbstractKmtronicRelay.java=UTF-8 +encoding//src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8Port.java=UTF-8 +encoding//src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8PortImpl.java=UTF-8 encoding/bnd.bnd=UTF-8 diff --git a/io.openems.edge.io.kmtronic/readme.adoc b/io.openems.edge.io.kmtronic/readme.adoc index ad60439208f..f0951328600 100644 --- a/io.openems.edge.io.kmtronic/readme.adoc +++ b/io.openems.edge.io.kmtronic/readme.adoc @@ -1,20 +1,8 @@ = KMtronic Modbus Relay Board -This bundle implements the Kmtronic Modbus Relay board. Relay outputs can be used to turn ON/OFF lights, motors and signal alarms. This relay has 8 output channels. +This bundle implements the Kmtronic Modbus Relay board. Relay outputs can be used to turn ON/OFF lights, motors and signal alarms. Implementations are for 4 and 8 relais. Implemented Natures:: - DigitalOutput -== Configuration - -- `Id` Unique ID of this Component (id), e.g. "io0", "io1" -- `Modbus-ID` ID of Modbus bridge. (modbus.id) -- `Modbus Unit-ID` The Unit-ID of the Modbus device. (modbusUnitId) - -=== Example Configuration - -- `Id` : io0 -- `Modbus-ID` : modbus3 -- `Modbus Unit-ID` : 1 - https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.io.kmtronic[Source Code icon:github[]] \ No newline at end of file diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/AbstractKmtronicRelay.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/AbstractKmtronicRelay.java new file mode 100644 index 00000000000..16026fc2aa5 --- /dev/null +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/AbstractKmtronicRelay.java @@ -0,0 +1,73 @@ +package io.openems.edge.io.kmtronic; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.osgi.service.cm.ConfigurationAdmin; +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 io.openems.common.channel.AccessMode; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.common.channel.BooleanWriteChannel; +import io.openems.edge.common.channel.WriteChannel; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.io.api.DigitalOutput; + +public abstract class AbstractKmtronicRelay extends AbstractOpenemsModbusComponent + implements DigitalOutput, OpenemsComponent, ModbusSlave { + + @Reference + protected ConfigurationAdmin cm; + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + private final BooleanWriteChannel[] digitalOutputChannels; + + protected AbstractKmtronicRelay(io.openems.edge.common.channel.ChannelId[] kmtronicChannelIds) { + super(// + OpenemsComponent.ChannelId.values(), // + DigitalOutput.ChannelId.values(), // + kmtronicChannelIds // + ); + this.digitalOutputChannels = Stream.of(kmtronicChannelIds) // + .filter(channelId -> channelId.doc().getAccessMode() == AccessMode.READ_WRITE) // + .map(channelId -> this.channel(channelId)) // + .toArray(BooleanWriteChannel[]::new); + } + + @Override + public BooleanWriteChannel[] digitalOutputChannels() { + return this.digitalOutputChannels; + } + + @Override + public String debugLog() { + StringBuilder b = new StringBuilder(); + int i = 1; + for (WriteChannel channel : this.digitalOutputChannels) { + String valueText; + Optional valueOpt = channel.value().asOptional(); + if (valueOpt.isPresent()) { + valueText = valueOpt.get() ? "x" : "-"; + } else { + valueText = "?"; + } + b.append(i + valueText); + + // add space for all but the last + if (++i <= this.digitalOutputChannels.length) { + b.append(" "); + } + } + return b.toString(); + } + +} diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java deleted file mode 100644 index 44af021b251..00000000000 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/KmtronicRelayOutput.java +++ /dev/null @@ -1,155 +0,0 @@ -package io.openems.edge.io.kmtronic; - -import java.util.Optional; - -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.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.metatype.annotations.Designate; - -import io.openems.common.channel.AccessMode; -import io.openems.common.exceptions.OpenemsException; -import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; -import io.openems.edge.bridge.modbus.api.BridgeModbus; -import io.openems.edge.bridge.modbus.api.ModbusProtocol; -import io.openems.edge.bridge.modbus.api.element.CoilElement; -import io.openems.edge.bridge.modbus.api.task.FC1ReadCoilsTask; -import io.openems.edge.bridge.modbus.api.task.FC5WriteCoilTask; -import io.openems.edge.common.channel.BooleanWriteChannel; -import io.openems.edge.common.channel.WriteChannel; -import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.modbusslave.ModbusSlave; -import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; -import io.openems.edge.common.modbusslave.ModbusSlaveTable; -import io.openems.edge.common.modbusslave.ModbusType; -import io.openems.edge.common.taskmanager.Priority; -import io.openems.edge.io.api.DigitalOutput; - -@Designate(ocd = Config.class, factory = true) -@Component(name = "IO.KMtronic", immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE) -public class KmtronicRelayOutput extends AbstractOpenemsModbusComponent - implements DigitalOutput, OpenemsComponent, ModbusSlave { - - @Reference - protected ConfigurationAdmin cm; - - @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) - protected void setModbus(BridgeModbus modbus) { - super.setModbus(modbus); - } - - private final BooleanWriteChannel[] digitalOutputChannels; - - public KmtronicRelayOutput() { - super(// - OpenemsComponent.ChannelId.values(), // - DigitalOutput.ChannelId.values(), // - ThisChannelId.values() // - ); - this.digitalOutputChannels = new BooleanWriteChannel[] { // - this.channel(ThisChannelId.RELAY_1), // - this.channel(ThisChannelId.RELAY_2), // - this.channel(ThisChannelId.RELAY_3), // - this.channel(ThisChannelId.RELAY_4), // - this.channel(ThisChannelId.RELAY_5), // - this.channel(ThisChannelId.RELAY_6), // - this.channel(ThisChannelId.RELAY_7), // - this.channel(ThisChannelId.RELAY_8), // - }; - } - - @Activate - void activate(ComponentContext context, Config config) throws OpenemsException { - if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, - "Modbus", config.modbus_id())) { - return; - } - } - - @Deactivate - protected void deactivate() { - super.deactivate(); - } - - @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { - return new ModbusProtocol(this, // - /* - * For Read: Read Coils - */ - new FC1ReadCoilsTask(0, Priority.LOW, // - m(ThisChannelId.RELAY_1, new CoilElement(0)), // - m(ThisChannelId.RELAY_2, new CoilElement(1)), // - m(ThisChannelId.RELAY_3, new CoilElement(2)), // - m(ThisChannelId.RELAY_4, new CoilElement(3)), // - m(ThisChannelId.RELAY_5, new CoilElement(4)), // - m(ThisChannelId.RELAY_6, new CoilElement(5)), // - m(ThisChannelId.RELAY_7, new CoilElement(6)), // - m(ThisChannelId.RELAY_8, new CoilElement(7)) // - ), - /* - * For Write: Write Single Coil - */ - new FC5WriteCoilTask(0, m(ThisChannelId.RELAY_1, new CoilElement(0))), // - new FC5WriteCoilTask(1, m(ThisChannelId.RELAY_2, new CoilElement(1))), // - new FC5WriteCoilTask(2, m(ThisChannelId.RELAY_3, new CoilElement(2))), // - new FC5WriteCoilTask(3, m(ThisChannelId.RELAY_4, new CoilElement(3))), // - new FC5WriteCoilTask(4, m(ThisChannelId.RELAY_5, new CoilElement(4))), // - new FC5WriteCoilTask(5, m(ThisChannelId.RELAY_6, new CoilElement(5))), // - new FC5WriteCoilTask(6, m(ThisChannelId.RELAY_7, new CoilElement(6))), // - new FC5WriteCoilTask(7, m(ThisChannelId.RELAY_8, new CoilElement(7))) // - ); - } - - @Override - public BooleanWriteChannel[] digitalOutputChannels() { - return this.digitalOutputChannels; - } - - @Override - public String debugLog() { - StringBuilder b = new StringBuilder(); - int i = 1; - for (WriteChannel channel : this.digitalOutputChannels) { - String valueText; - Optional valueOpt = channel.value().asOptional(); - if (valueOpt.isPresent()) { - valueText = valueOpt.get() ? "x" : "-"; - } else { - valueText = "?"; - } - b.append(i + valueText); - - // add space for all but the last - if (++i <= this.digitalOutputChannels.length) { - b.append(" "); - } - } - return b.toString(); - } - - @Override - public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { - return new ModbusSlaveTable( // - OpenemsComponent.getModbusSlaveNatureTable(accessMode), // - ModbusSlaveNatureTable.of(KmtronicRelayOutput.class, accessMode, 100)// - .channel(0, ThisChannelId.RELAY_1, ModbusType.UINT16) // - .channel(1, ThisChannelId.RELAY_2, ModbusType.UINT16) // - .channel(2, ThisChannelId.RELAY_3, ModbusType.UINT16) // - .channel(3, ThisChannelId.RELAY_4, ModbusType.UINT16) // - .channel(4, ThisChannelId.RELAY_5, ModbusType.UINT16) // - .channel(5, ThisChannelId.RELAY_6, ModbusType.UINT16) // - .channel(6, ThisChannelId.RELAY_7, ModbusType.UINT16) // - .channel(7, ThisChannelId.RELAY_8, ModbusType.UINT16) // - .build()// - ); - } - -} diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/ThisChannelId.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/ThisChannelId.java deleted file mode 100644 index 5ffac5d2eb2..00000000000 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/ThisChannelId.java +++ /dev/null @@ -1,196 +0,0 @@ -package io.openems.edge.io.kmtronic; - -import io.openems.common.channel.AccessMode; -import io.openems.common.types.OpenemsType; -import io.openems.edge.common.channel.BooleanDoc; -import io.openems.edge.common.channel.BooleanWriteChannel; -import io.openems.edge.common.channel.Doc; - -public enum ThisChannelId implements io.openems.edge.common.channel.ChannelId { - /** - * Holds writes to Relay Output 1 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_1(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 1 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_1(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_1))), // - /** - * Holds writes to Relay Output 2 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_2(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 2 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_2(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_2))), // - /** - * Holds writes to Relay Output 3 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_3(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 3 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_3(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_3))), // - /** - * Holds writes to Relay Output 4 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_4(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 4 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_4(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_4))), // - /** - * Holds writes to Relay Output 5 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_5(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 5 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_5(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_5))), // - /** - * Holds writes to Relay Output 6 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_6(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 6 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_6(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_6))), // - /** - * Holds writes to Relay Output 7 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_7(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 7 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_7(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_7))), // - /** - * Holds writes to Relay Output 8 for debugging - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - DEBUG_RELAY_8(Doc.of(OpenemsType.BOOLEAN)), // - /** - * Relay Output 8 - * - *
      - *
    • Interface: KmtronicRelayOutput - *
    • Type: Boolean - *
    • Range: On/Off - *
    - */ - RELAY_8(new BooleanDoc() // - .accessMode(AccessMode.READ_WRITE) // - .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ThisChannelId.DEBUG_RELAY_8))); // - - private final Doc doc; - - private ThisChannelId(Doc doc) { - this.doc = doc; - } - - public Doc doc() { - return this.doc; - } -} \ No newline at end of file diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/Config.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/Config.java new file mode 100644 index 00000000000..a1088deb7ad --- /dev/null +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/Config.java @@ -0,0 +1,30 @@ +package io.openems.edge.io.kmtronic.eight; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition( // + name = "IO KMtronic 8-Port Relay Board", // + description = "Implements the KMtronic 8-Port Relay Board.") +@interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "io0"; + + @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 = "Modbus-ID", description = "ID of Modbus bridge.") + String modbus_id() default "modbus0"; + + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") + int modbusUnitId() default 1; + + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") + String Modbus_target() default ""; + + String webconsole_configurationFactory_nameHint() default "IO KMtronic 8-Port Relay Board [{id}]"; +} \ No newline at end of file diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8Port.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8Port.java new file mode 100644 index 00000000000..ab8efdbe963 --- /dev/null +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8Port.java @@ -0,0 +1,218 @@ +package io.openems.edge.io.kmtronic.eight; + +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; +import io.openems.common.types.OpenemsType; +import io.openems.edge.common.channel.BooleanDoc; +import io.openems.edge.common.channel.BooleanWriteChannel; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.io.api.DigitalOutput; + +public interface KmtronicRelay8Port extends DigitalOutput, OpenemsComponent, ModbusSlave { + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + /** + * Holds writes to Relay Output 1 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_1(Doc.of(OpenemsType.BOOLEAN)), // + /** + * Relay Output 1 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_1(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_1))), // + /** + * Holds writes to Relay Output 2 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_2(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 2 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_2(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_2))), // + /** + * Holds writes to Relay Output 3 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_3(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 3 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_3(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_3))), // + /** + * Holds writes to Relay Output 4 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_4(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 4 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_4(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_4))), // + /** + * Holds writes to Relay Output 5 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_5(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 5 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_5(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_5))), // + /** + * Holds writes to Relay Output 6 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_6(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 6 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_6(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_6))), // + /** + * Holds writes to Relay Output 7 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_7(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 7 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_7(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_7))), // + /** + * Holds writes to Relay Output 8 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_8(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 8 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_8(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_8))); // + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + public Doc doc() { + return this.doc; + } + } +} diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8PortImpl.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8PortImpl.java new file mode 100644 index 00000000000..5eb33f0b692 --- /dev/null +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/KmtronicRelay8PortImpl.java @@ -0,0 +1,112 @@ +package io.openems.edge.io.kmtronic.eight; + +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.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.metatype.annotations.Designate; + +import io.openems.common.channel.AccessMode; +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.CoilElement; +import io.openems.edge.bridge.modbus.api.task.FC1ReadCoilsTask; +import io.openems.edge.bridge.modbus.api.task.FC5WriteCoilTask; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; +import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.modbusslave.ModbusType; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.io.api.DigitalOutput; +import io.openems.edge.io.kmtronic.AbstractKmtronicRelay; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "IO.KMtronic", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +public class KmtronicRelay8PortImpl extends AbstractKmtronicRelay + implements KmtronicRelay8Port, DigitalOutput, OpenemsComponent, ModbusSlave { + + @Reference + protected ConfigurationAdmin cm; + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + public KmtronicRelay8PortImpl() { + super(KmtronicRelay8Port.ChannelId.values()); + } + + @Activate + void activate(ComponentContext context, Config config) throws OpenemsException { + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id())) { + return; + } + } + + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + return new ModbusProtocol(this, // + /* + * For Read: Read Coils + */ + new FC1ReadCoilsTask(0, Priority.LOW, // + m(KmtronicRelay8Port.ChannelId.RELAY_1, new CoilElement(0)), // + m(KmtronicRelay8Port.ChannelId.RELAY_2, new CoilElement(1)), // + m(KmtronicRelay8Port.ChannelId.RELAY_3, new CoilElement(2)), // + m(KmtronicRelay8Port.ChannelId.RELAY_4, new CoilElement(3)), // + m(KmtronicRelay8Port.ChannelId.RELAY_5, new CoilElement(4)), // + m(KmtronicRelay8Port.ChannelId.RELAY_6, new CoilElement(5)), // + m(KmtronicRelay8Port.ChannelId.RELAY_7, new CoilElement(6)), // + m(KmtronicRelay8Port.ChannelId.RELAY_8, new CoilElement(7)) // + ), + /* + * For Write: Write Single Coil + */ + new FC5WriteCoilTask(0, m(KmtronicRelay8Port.ChannelId.RELAY_1, new CoilElement(0))), // + new FC5WriteCoilTask(1, m(KmtronicRelay8Port.ChannelId.RELAY_2, new CoilElement(1))), // + new FC5WriteCoilTask(2, m(KmtronicRelay8Port.ChannelId.RELAY_3, new CoilElement(2))), // + new FC5WriteCoilTask(3, m(KmtronicRelay8Port.ChannelId.RELAY_4, new CoilElement(3))), // + new FC5WriteCoilTask(4, m(KmtronicRelay8Port.ChannelId.RELAY_5, new CoilElement(4))), // + new FC5WriteCoilTask(5, m(KmtronicRelay8Port.ChannelId.RELAY_6, new CoilElement(5))), // + new FC5WriteCoilTask(6, m(KmtronicRelay8Port.ChannelId.RELAY_7, new CoilElement(6))), // + new FC5WriteCoilTask(7, m(KmtronicRelay8Port.ChannelId.RELAY_8, new CoilElement(7))) // + ); + } + + @Override + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { + return new ModbusSlaveTable( // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(KmtronicRelay8Port.class, accessMode, 100)// + .channel(0, KmtronicRelay8Port.ChannelId.RELAY_1, ModbusType.UINT16) // + .channel(1, KmtronicRelay8Port.ChannelId.RELAY_2, ModbusType.UINT16) // + .channel(2, KmtronicRelay8Port.ChannelId.RELAY_3, ModbusType.UINT16) // + .channel(3, KmtronicRelay8Port.ChannelId.RELAY_4, ModbusType.UINT16) // + .channel(4, KmtronicRelay8Port.ChannelId.RELAY_5, ModbusType.UINT16) // + .channel(5, KmtronicRelay8Port.ChannelId.RELAY_6, ModbusType.UINT16) // + .channel(6, KmtronicRelay8Port.ChannelId.RELAY_7, ModbusType.UINT16) // + .channel(7, KmtronicRelay8Port.ChannelId.RELAY_8, ModbusType.UINT16) // + .build()// + ); + } + +} diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/Config.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/Config.java similarity index 84% rename from io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/Config.java rename to io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/Config.java index e5c55f617b9..d20938818cb 100644 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/Config.java +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/Config.java @@ -1,11 +1,11 @@ -package io.openems.edge.io.kmtronic; +package io.openems.edge.io.kmtronic.four; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition( // - name = "IO KMtronic Relay Board", // - description = "Implements the KMtronic Relay Board.") + name = "IO KMtronic 4-Port Relay Board", // + description = "Implements the KMtronic 4-Port Relay Board.") @interface Config { @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") @@ -26,5 +26,5 @@ @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default ""; - String webconsole_configurationFactory_nameHint() default "IO KMtronic Relay Board [{id}]"; + String webconsole_configurationFactory_nameHint() default "IO KMtronic 4-Port Relay Board [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/KmtronicRelay4Port.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/KmtronicRelay4Port.java new file mode 100644 index 00000000000..ef205dcf5be --- /dev/null +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/KmtronicRelay4Port.java @@ -0,0 +1,122 @@ +package io.openems.edge.io.kmtronic.four; + +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; +import io.openems.common.types.OpenemsType; +import io.openems.edge.common.channel.BooleanDoc; +import io.openems.edge.common.channel.BooleanWriteChannel; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.io.api.DigitalOutput; + +public interface KmtronicRelay4Port extends DigitalOutput, OpenemsComponent, ModbusSlave { + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + /** + * Holds writes to Relay Output 1 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_1(Doc.of(OpenemsType.BOOLEAN)), // + /** + * Relay Output 1 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_1(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_1))), // + /** + * Holds writes to Relay Output 2 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_2(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 2 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_2(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_2))), // + /** + * Holds writes to Relay Output 3 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_3(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 3 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_3(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_3))), // + /** + * Holds writes to Relay Output 4 for debugging + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY_4(Doc.of(OpenemsType.BOOLEAN) // + .persistencePriority(PersistencePriority.MEDIUM)), // + /** + * Relay Output 4 + * + *
      + *
    • Interface: KmtronicRelayOutput + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY_4(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // + .onInit(new BooleanWriteChannel.MirrorToDebugChannel(ChannelId.DEBUG_RELAY_4))); // + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + public Doc doc() { + return this.doc; + } + } +} diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/KmtronicRelay4PortImpl.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/KmtronicRelay4PortImpl.java new file mode 100644 index 00000000000..9208826c0b8 --- /dev/null +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/KmtronicRelay4PortImpl.java @@ -0,0 +1,100 @@ +package io.openems.edge.io.kmtronic.four; + +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.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.metatype.annotations.Designate; + +import io.openems.common.channel.AccessMode; +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.CoilElement; +import io.openems.edge.bridge.modbus.api.task.FC1ReadCoilsTask; +import io.openems.edge.bridge.modbus.api.task.FC5WriteCoilTask; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; +import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.modbusslave.ModbusType; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.io.api.DigitalOutput; +import io.openems.edge.io.kmtronic.AbstractKmtronicRelay; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "IO.KMtronic.4Port", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +public class KmtronicRelay4PortImpl extends AbstractKmtronicRelay + implements KmtronicRelay4Port, DigitalOutput, OpenemsComponent, ModbusSlave { + + @Reference + protected ConfigurationAdmin cm; + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + public KmtronicRelay4PortImpl() { + super(KmtronicRelay4Port.ChannelId.values()); + } + + @Activate + void activate(ComponentContext context, Config config) throws OpenemsException { + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id())) { + return; + } + } + + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + return new ModbusProtocol(this, // + /* + * For Read: Read Coils + */ + new FC1ReadCoilsTask(0, Priority.LOW, // + m(KmtronicRelay4Port.ChannelId.RELAY_1, new CoilElement(0)), // + m(KmtronicRelay4Port.ChannelId.RELAY_2, new CoilElement(1)), // + m(KmtronicRelay4Port.ChannelId.RELAY_3, new CoilElement(2)), // + m(KmtronicRelay4Port.ChannelId.RELAY_4, new CoilElement(3)) // + ), + /* + * For Write: Write Single Coil + */ + new FC5WriteCoilTask(0, m(KmtronicRelay4Port.ChannelId.RELAY_1, new CoilElement(0))), // + new FC5WriteCoilTask(1, m(KmtronicRelay4Port.ChannelId.RELAY_2, new CoilElement(1))), // + new FC5WriteCoilTask(2, m(KmtronicRelay4Port.ChannelId.RELAY_3, new CoilElement(2))), // + new FC5WriteCoilTask(3, m(KmtronicRelay4Port.ChannelId.RELAY_4, new CoilElement(3))) // + ); + } + + @Override + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { + return new ModbusSlaveTable( // + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(KmtronicRelay4Port.class, accessMode, 100)// + .channel(0, KmtronicRelay4Port.ChannelId.RELAY_1, ModbusType.UINT16) // + .channel(1, KmtronicRelay4Port.ChannelId.RELAY_2, ModbusType.UINT16) // + .channel(2, KmtronicRelay4Port.ChannelId.RELAY_3, ModbusType.UINT16) // + .channel(3, KmtronicRelay4Port.ChannelId.RELAY_4, ModbusType.UINT16) // + .build()// + ); + } + +} diff --git a/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/eight/KmtronicRelay8PortTest.java b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/eight/KmtronicRelay8PortTest.java new file mode 100644 index 00000000000..05d8e80aab4 --- /dev/null +++ b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/eight/KmtronicRelay8PortTest.java @@ -0,0 +1,25 @@ +package io.openems.edge.io.kmtronic.eight; + +import org.junit.Test; + +import io.openems.edge.bridge.modbus.test.DummyModbusBridge; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyConfigurationAdmin; + +public class KmtronicRelay8PortTest { + + private static final String IO_ID = "io0"; + private static final String MODBUS_ID = "modbus0"; + + @Test + public void test() throws Exception { + new ComponentTest(new KmtronicRelay8PortImpl()) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // + .activate(MyConfig.create() // + .setId(IO_ID) // + .setModbusId(MODBUS_ID) // + .build()) // + ; + } +} diff --git a/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/eight/MyConfig.java b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/eight/MyConfig.java new file mode 100644 index 00000000000..61ca9092c2c --- /dev/null +++ b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/eight/MyConfig.java @@ -0,0 +1,64 @@ +package io.openems.edge.io.kmtronic.eight; + +import io.openems.common.utils.ConfigUtils; +import io.openems.edge.common.test.AbstractComponentConfig; +import io.openems.edge.io.kmtronic.eight.Config; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + protected static class Builder { + private String id = null; + private String modbusId = null; + public int modbusUnitId; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setModbusId(String modbusId) { + this.modbusId = modbusId; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public String modbus_id() { + return this.builder.modbusId; + } + + @Override + public String Modbus_target() { + return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id()); + } + + @Override + public int modbusUnitId() { + return this.builder.modbusUnitId; + } + +} \ No newline at end of file diff --git a/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/KmtronicRelayOutputTest.java b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/four/KmtronicRelay4PortTest.java similarity index 81% rename from io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/KmtronicRelayOutputTest.java rename to io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/four/KmtronicRelay4PortTest.java index 672b43b478a..83f4af7a7ab 100644 --- a/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/KmtronicRelayOutputTest.java +++ b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/four/KmtronicRelay4PortTest.java @@ -1,4 +1,4 @@ -package io.openems.edge.io.kmtronic; +package io.openems.edge.io.kmtronic.four; import org.junit.Test; @@ -6,14 +6,14 @@ import io.openems.edge.common.test.ComponentTest; import io.openems.edge.common.test.DummyConfigurationAdmin; -public class KmtronicRelayOutputTest { +public class KmtronicRelay4PortTest { private static final String IO_ID = "io0"; private static final String MODBUS_ID = "modbus0"; @Test public void test() throws Exception { - new ComponentTest(new KmtronicRelayOutput()) // + new ComponentTest(new KmtronicRelay4PortImpl()) // .addReference("cm", new DummyConfigurationAdmin()) // .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // .activate(MyConfig.create() // diff --git a/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/MyConfig.java b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/four/MyConfig.java similarity index 96% rename from io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/MyConfig.java rename to io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/four/MyConfig.java index e7f20e26874..4d95e0cceb9 100644 --- a/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/MyConfig.java +++ b/io.openems.edge.io.kmtronic/test/io/openems/edge/io/kmtronic/four/MyConfig.java @@ -1,4 +1,4 @@ -package io.openems.edge.io.kmtronic; +package io.openems.edge.io.kmtronic.four; import io.openems.common.utils.ConfigUtils; import io.openems.edge.common.test.AbstractComponentConfig; diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus4xxDI.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus4xxDI.java index a2a3fae7a44..ab1a6f049cd 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus4xxDI.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus4xxDI.java @@ -1,5 +1,6 @@ package io.openems.edge.wago; +import io.openems.common.channel.PersistencePriority; import io.openems.edge.bridge.modbus.api.element.CoilElement; import io.openems.edge.bridge.modbus.api.element.ModbusCoilElement; import io.openems.edge.common.channel.BooleanDoc; @@ -21,6 +22,7 @@ public Fieldbus4xxDI(Wago parent, int moduleCount, int coilOffset0, int channels this.inputCoil0Elements = new ModbusCoilElement[channelsCount]; for (int i = 0; i < channelsCount; i++) { BooleanDoc doc = new BooleanDoc(); + doc.persistencePriority(PersistencePriority.MEDIUM); FieldbusChannelId channelId = new FieldbusChannelId(id + "_C" + (i + 1), doc); BooleanReadChannel channel = parent.addChannel(channelId); this.readChannels[i] = channel; diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java index d9d35dd773f..b499b1899ae 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus523RO1Ch.java @@ -1,6 +1,7 @@ package io.openems.edge.wago; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.edge.bridge.modbus.api.element.DummyCoilElement; import io.openems.edge.bridge.modbus.api.element.ModbusCoilElement; import io.openems.edge.common.channel.BooleanDoc; @@ -24,6 +25,7 @@ public Fieldbus523RO1Ch(Wago parent, int moduleCount, int coilOffset0, int coilO { OpenemsTypeDoc doc = new BooleanDoc() // .accessMode(AccessMode.READ_WRITE); + doc.persistencePriority(PersistencePriority.MEDIUM); FieldbusChannelId channelId = new FieldbusChannelId(id, doc); channel1 = (BooleanWriteChannel) parent.addChannel(channelId); } diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus5xxDO.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus5xxDO.java index b8a7068fbf2..e5198331ae5 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus5xxDO.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/Fieldbus5xxDO.java @@ -1,6 +1,7 @@ package io.openems.edge.wago; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.edge.bridge.modbus.api.element.ModbusCoilElement; import io.openems.edge.common.channel.BooleanDoc; import io.openems.edge.common.channel.BooleanReadChannel; @@ -26,6 +27,7 @@ public Fieldbus5xxDO(Wago parent, int moduleCount, int coilOffset512, int channe for (int i = 0; i < channelsCount; i++) { OpenemsTypeDoc doc = new BooleanDoc() // .accessMode(AccessMode.READ_WRITE); + doc.persistencePriority(PersistencePriority.MEDIUM); FieldbusChannelId channelId = new FieldbusChannelId(id + "_C" + (i + 1), doc); BooleanWriteChannel channel = (BooleanWriteChannel) parent.addChannel(channelId); diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java index d93ee31b97a..10c0cbb3693 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/AsymmetricMeter.java @@ -3,6 +3,7 @@ import java.util.function.Consumer; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; @@ -41,6 +42,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT)), // /** * Active Power L2 @@ -56,6 +58,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT)), // /** * Active Power L3 @@ -71,6 +74,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT)), // /** * Reactive Power L1 @@ -86,6 +90,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER_L1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT)), // /** * Reactive Power L2 @@ -101,6 +106,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER_L2(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT)), // /** * Reactive Power L3 @@ -116,6 +122,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER_L3(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(POWER_DOC_TEXT)), // /** * Voltage L1 @@ -127,7 +134,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ VOLTAGE_L1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT)), // + .unit(Unit.MILLIVOLT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Voltage L2 * @@ -138,7 +146,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ VOLTAGE_L2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT)), // + .unit(Unit.MILLIVOLT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Voltage L3 * @@ -149,7 +158,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ VOLTAGE_L3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT)), // + .unit(Unit.MILLIVOLT) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Current L1 * @@ -160,7 +170,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CURRENT_L1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // + .unit(Unit.MILLIAMPERE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Current L2 * @@ -171,7 +182,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CURRENT_L2(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)), // + .unit(Unit.MILLIAMPERE) // + .persistencePriority(PersistencePriority.HIGH)), // /** * Current L3 * @@ -182,7 +194,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CURRENT_L3(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)); // + .unit(Unit.MILLIAMPERE) // + .persistencePriority(PersistencePriority.HIGH)); // private final Doc doc; diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java index 20c663a4086..4d1ec9891fa 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SymmetricMeter.java @@ -2,6 +2,7 @@ import io.openems.common.OpenemsConstants; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; import io.openems.common.utils.IntUtils; @@ -41,7 +42,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ FREQUENCY(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIHERTZ)), // + .unit(Unit.MILLIHERTZ) // + .persistencePriority(PersistencePriority.HIGH)), /** * Minimum Ever Active Power. * @@ -54,7 +56,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ MIN_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), /** * Maximum Ever Active Power. * @@ -67,7 +70,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ MAX_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.WATT)), // + .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH)), /** * Active Power. * @@ -82,6 +86,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ ACTIVE_POWER(new IntegerDoc() // .unit(Unit.WATT) // + .persistencePriority(PersistencePriority.HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT) // .onInit(channel -> { channel.onSetNextValue(value -> { @@ -115,6 +120,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { } }); })), // + /** * Reactive Power. * @@ -129,6 +135,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { */ REACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT_AMPERE_REACTIVE) // + .persistencePriority(PersistencePriority.HIGH) // .text(OpenemsConstants.POWER_DOC_TEXT)), // /** * Active Production Energy. @@ -140,7 +147,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ACTIVE_PRODUCTION_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)), /** * Active Consumption Energy. * @@ -151,7 +159,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ ACTIVE_CONSUMPTION_ENERGY(Doc.of(OpenemsType.LONG) // - .unit(Unit.WATT_HOURS)), + .unit(Unit.WATT_HOURS) // + .persistencePriority(PersistencePriority.HIGH)), /** * Voltage. * @@ -162,7 +171,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ VOLTAGE(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIVOLT)), // + .unit(Unit.MILLIVOLT) // + .persistencePriority(PersistencePriority.HIGH)), /** * Current. * @@ -173,7 +183,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ CURRENT(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.MILLIAMPERE)); // + .unit(Unit.MILLIAMPERE) // + .persistencePriority(PersistencePriority.HIGH)); private final Doc doc; diff --git a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesRequest.java b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesRequest.java index 2eb5b92b98a..18b328dcb41 100644 --- a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesRequest.java +++ b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesRequest.java @@ -1,7 +1,5 @@ package io.openems.edge.meter.discovergy.jsonrpc; -import java.util.UUID; - import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; @@ -24,24 +22,37 @@ */ public class GetFieldNamesRequest extends JsonrpcRequest { + public static final String METHOD = "getFieldNames"; + + /** + * Create {@link GetFieldNamesRequest} from a template {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link GetFieldNamesRequest} + * @throws OpenemsNamedException on parse error + */ public static GetFieldNamesRequest from(JsonrpcRequest r) throws OpenemsNamedException { String meterId = JsonUtils.getAsString(r.getParams(), "meterId"); - return new GetFieldNamesRequest(r.getId(), meterId); + return new GetFieldNamesRequest(r, meterId); } - public static final String METHOD = "getFieldNames"; - private final String meterId; public GetFieldNamesRequest(String meterId) { - this(UUID.randomUUID(), meterId); + super(METHOD); + this.meterId = meterId; } - public GetFieldNamesRequest(UUID id, String meterId) { - super(id, METHOD); + private GetFieldNamesRequest(JsonrpcRequest request, String meterId) { + super(request, METHOD); this.meterId = meterId; } + /** + * Gets the Meter-ID. + * + * @return Meter-ID + */ public String getMeterId() { return this.meterId; } diff --git a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesResponse.java b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesResponse.java index 31852ae501f..21375bbe6a4 100644 --- a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesResponse.java +++ b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetFieldNamesResponse.java @@ -48,8 +48,13 @@ public JsonObject getResult() { .build(); } + /** + * Gets the {@link Field}s + * + * @return a set of Fields + */ public Set getFields() { - return fields; + return this.fields; } } diff --git a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetMetersRequest.java b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetMetersRequest.java index 98b9791f2de..c5d9857a84a 100644 --- a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetMetersRequest.java +++ b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/jsonrpc/GetMetersRequest.java @@ -1,10 +1,9 @@ package io.openems.edge.meter.discovergy.jsonrpc; -import java.util.UUID; - import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.JsonrpcRequest; /** @@ -21,18 +20,25 @@ */ public class GetMetersRequest extends JsonrpcRequest { + public static final String METHOD = "getMeters"; + + /** + * Create {@link GetMetersRequest} from a template {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link GetMetersRequest} + * @throws OpenemsNamedException on parse error + */ public static GetMetersRequest from(JsonrpcRequest r) throws OpenemsException { - return new GetMetersRequest(r.getId()); + return new GetMetersRequest(r); } - public static final String METHOD = "getMeters"; - public GetMetersRequest() { - this(UUID.randomUUID()); + super(METHOD); } - public GetMetersRequest(UUID id) { - super(id, METHOD); + private GetMetersRequest(JsonrpcRequest request) { + super(request, METHOD); } @Override diff --git a/io.openems.edge.pvinverter.api/src/io/openems/edge/pvinverter/api/ManagedSymmetricPvInverter.java b/io.openems.edge.pvinverter.api/src/io/openems/edge/pvinverter/api/ManagedSymmetricPvInverter.java index daacb69c86c..91ae01ebc09 100644 --- a/io.openems.edge.pvinverter.api/src/io/openems/edge/pvinverter/api/ManagedSymmetricPvInverter.java +++ b/io.openems.edge.pvinverter.api/src/io/openems/edge/pvinverter/api/ManagedSymmetricPvInverter.java @@ -1,6 +1,7 @@ package io.openems.edge.pvinverter.api; import io.openems.common.channel.AccessMode; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.types.OpenemsType; @@ -31,7 +32,8 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { * */ MAX_APPARENT_POWER(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT_AMPERE)), // + .unit(Unit.VOLT_AMPERE) // + .persistencePriority(PersistencePriority.MEDIUM)), // /** * Read/Set Active Power Limit. * @@ -44,6 +46,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { ACTIVE_POWER_LIMIT(new IntegerDoc() // .unit(Unit.WATT) // .accessMode(AccessMode.READ_WRITE) // + .persistencePriority(PersistencePriority.MEDIUM) // .onInit(channel -> { // // on each Write to the channel -> set the value ((IntegerWriteChannel) channel).onSetNextWrite(value -> { diff --git a/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/Scheduler.java b/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/Scheduler.java index a6109be9dcf..bd80cb1fe87 100644 --- a/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/Scheduler.java +++ b/io.openems.edge.scheduler.api/src/io/openems/edge/scheduler/api/Scheduler.java @@ -5,6 +5,7 @@ import org.osgi.annotation.versioning.ProviderType; import io.openems.common.channel.Level; +import io.openems.common.channel.PersistencePriority; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; @@ -15,6 +16,7 @@ public interface Scheduler extends OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { CONTROLLER_IS_MISSING(Doc.of(Level.INFO) // + .persistencePriority(PersistencePriority.HIGH) // .text("A configured Controller is missing")); private final Doc doc; diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/ExecuteSimulationRequest.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/ExecuteSimulationRequest.java index 9fca9865263..5323f526358 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/ExecuteSimulationRequest.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/ExecuteSimulationRequest.java @@ -54,7 +54,17 @@ */ public class ExecuteSimulationRequest extends JsonrpcRequest { + public static final String METHOD = "executeSimulation"; + public static class Clock { + + /** + * Create {@link Clock} from {@link JsonObject}. + * + * @param j the {@link JsonObject} + * @return the {@link Clock} + * @throws OpenemsNamedException on parse error + */ public static Clock from(JsonObject j) throws OpenemsNamedException { ZonedDateTime start = ZonedDateTime.parse(JsonUtils.getAsString(j, "start")); ZonedDateTime end = ZonedDateTime.parse(JsonUtils.getAsString(j, "end")); @@ -77,6 +87,14 @@ private Clock(ZonedDateTime start, ZonedDateTime end, int timeleapPerCycle, bool } public static class Profile { + + /** + * Create {@link Profile} from {@link JsonArray}. + * + * @param j the {@link JsonArray} + * @return the {@link Profile} + * @throws OpenemsNamedException on parse error + */ public static Profile from(JsonArray j) throws OpenemsNamedException { final List values = new ArrayList<>(); j.forEach(value -> { @@ -95,10 +113,18 @@ private Profile(List values) { this.values = values; } + /** + * Gets the currently active value of the {@link Profile}. + * + * @return the value + */ public synchronized Integer getCurrentValue() { return this.values.get(this.currentIndex); } + /** + * Selects the next active value in the {@link Profile}. + */ public synchronized void selectNextValue() { this.currentIndex += 1; if (this.currentIndex > this.values.size() - 1) { @@ -108,8 +134,14 @@ public synchronized void selectNextValue() { } - public static final String METHOD = "executeSimulation"; - + /** + * Create {@link ExecuteSimulationRequest} from a template + * {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link ExecuteSimulationRequest} + * @throws OpenemsNamedException on parse error + */ public static ExecuteSimulationRequest from(JsonrpcRequest r) throws OpenemsNamedException { JsonObject p = r.getParams(); List components = new ArrayList<>(); @@ -128,7 +160,7 @@ public static ExecuteSimulationRequest from(JsonrpcRequest r) throws OpenemsName for (JsonElement jCollect : jCollects) { collects.add(ChannelAddress.fromString(JsonUtils.getAsString(jCollect))); } - return new ExecuteSimulationRequest(r.getId(), components, clock, profiles, collects); + return new ExecuteSimulationRequest(r, components, clock, profiles, collects); } public final List components; @@ -138,12 +170,16 @@ public static ExecuteSimulationRequest from(JsonrpcRequest r) throws OpenemsName public ExecuteSimulationRequest(List components, Clock clock, Map profiles, List collects) { - this(UUID.randomUUID(), components, clock, profiles, collects); + super(UUID.randomUUID(), METHOD, JsonrpcRequest.NO_TIMEOUT); + this.components = components; + this.clock = clock; + this.profiles = profiles; + this.collects = collects; } - public ExecuteSimulationRequest(UUID id, List components, Clock clock, + public ExecuteSimulationRequest(JsonrpcRequest request, List components, Clock clock, Map profiles, List collects) { - super(id, METHOD); + super(request, METHOD); this.components = components; this.clock = clock; this.profiles = profiles; diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java index ee7f4e5a84a..5ecb974b0e6 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java @@ -293,8 +293,8 @@ private void collectData() { /** * Apply simulated Time-Leap per Cycle. * - * @param clock the {@link TimeLeapClock} - * @param currentSimulation the current {@link ExecuteSimulationRequest} + * @param clock the {@link TimeLeapClock} + * @param currentSimulationRequest the current {@link ExecuteSimulationRequest} */ private void applyTimeLeap(TimeLeapClock clock, ExecuteSimulationRequest currentSimulationRequest) { if (currentSimulationRequest.clock.executeCycleTwice) { @@ -322,6 +322,7 @@ private void applyTimeLeap(TimeLeapClock clock, ExecuteSimulationRequest current /** * Delete all non-required Components. * + * @param user the {@link User} * @throws OpenemsNamedException on error */ private void deleteAllConfigurations(User user) throws OpenemsNamedException { @@ -352,8 +353,6 @@ private void deleteAllConfigurations(User user) throws OpenemsNamedException { /** * Stop the Simulation. - * - * @param currentSimulation the current simulation */ private void stopSimulation() { this.logInfo(this.log, "Stopping Simulation"); diff --git a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Config.java b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Config.java index e5e8c837e21..619093cd17d 100644 --- a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Config.java +++ b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Config.java @@ -3,6 +3,8 @@ import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import io.openems.common.channel.PersistencePriority; + @ObjectClassDefinition(// name = "Timedata RRD4J", // description = "This component persists data to RRD4J files.") @@ -17,8 +19,8 @@ @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") boolean enabled() default true; - @AttributeDefinition(name = "No. of Cycles", description = "How many Cycles till data is recorded.") - int noOfCycles() default RecordWorker.DEFAULT_NO_OF_CYCLES; + @AttributeDefinition(name = "Persistence Priority", description = "Store only Channels with a Persistence Priority above this. Be aware that too many writes can wear-out your flash storage.") + PersistencePriority persistencePriority() default PersistencePriority.MEDIUM; String webconsole_configurationFactory_nameHint() default "Timedata RRD4J [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/RecordWorker.java b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/RecordWorker.java index cb4d09601cb..265a177b420 100644 --- a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/RecordWorker.java +++ b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/RecordWorker.java @@ -1,6 +1,7 @@ package io.openems.edge.timedata.rrd4j; import java.io.IOException; +import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; @@ -21,6 +22,7 @@ import io.openems.common.types.OpenemsType; import io.openems.common.worker.AbstractImmediateWorker; import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.OpenemsComponent; public class RecordWorker extends AbstractImmediateWorker { @@ -29,7 +31,6 @@ public class RecordWorker extends AbstractImmediateWorker { private final Logger log = LoggerFactory.getLogger(RecordWorker.class); private final Rrd4jTimedataImpl parent; - protected int noOfCycles = DEFAULT_NO_OF_CYCLES; // default, is going to be overwritten by config // Counts the number of Cycles till data is recorded private int cycleCount = 0; @@ -77,25 +78,28 @@ public void collectData() { return; } - // Stop here if not reached CycleCount - if (this.cycleCount < this.noOfCycles) { + if ( + // No need to persist data, as it is still stored by the Channel itself. The + // Channel keeps the last NO_OF_PAST_VALUES values + this.cycleCount < Channel.NO_OF_PAST_VALUES + // RRD4j requires us to write one value per DEFAULT_HEARTBEAT_SECONDS + && Duration.between(this.lastTimestamp, timestamp) + .getSeconds() < Rrd4jTimedataImpl.DEFAULT_HEARTBEAT_SECONDS - 1) { return; } - - // Reset Cycle-Count - this.cycleCount = 0; + this.cycleCount = 0; // Reset Cycle-Count this.lastTimestamp = timestamp; for (OpenemsComponent component : this.parent.componentManager.getEnabledComponents()) { for (Channel channel : component.channels()) { - if (channel.channelDoc().getAccessMode() != AccessMode.READ_ONLY - && channel.channelDoc().getAccessMode() != AccessMode.READ_WRITE) { - // Ignore WRITE_ONLY Channels + Doc doc = channel.channelDoc(); + if ( // Ignore Low-Priority Channels + doc.getPersistencePriority().isLowerThan(this.parent.persistencePriority) + // Ignore WRITE_ONLY Channels + || channel.channelDoc().getAccessMode() == AccessMode.WRITE_ONLY) { continue; } - - ToDoubleFunction channelMapFunction = this .getChannelMapFunction(channel.channelDoc().getType()); @@ -126,20 +130,21 @@ public void collectData() { } } } + this.readChannelValuesSince = nextReadChannelValuesSince; - this.triggerNextRun(); } @Override protected void forever() throws InterruptedException { Record record = this.records.take(); RrdDb database = null; + try { database = this.parent.getRrdDb(record.address, record.unit, record.timestamp - 1); if (database.getLastUpdateTime() < record.timestamp) { // Avoid and silently ignore error "IllegalArgumentException: Bad sample time: - // XXX. Last update time was YYY, at least one second step is required". + // YYY. Last update time was ZZZ, at least one second step is required". // Add Sample to RRD4J Sample sample = database.createSample(record.timestamp); @@ -254,8 +259,4 @@ private Function getChannelAggregateFunction(Unit throw new IllegalArgumentException("Channel Unit [" + channelUnit + "] is not supported."); } - public void setNoOfCycles(int noOfCycles) { - this.noOfCycles = noOfCycles; - } - } diff --git a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jTimedataImpl.java b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jTimedataImpl.java index 6d32e546cdd..0054f65a43f 100644 --- a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jTimedataImpl.java +++ b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jTimedataImpl.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Paths; import java.time.Instant; import java.time.ZoneId; @@ -32,6 +33,7 @@ import org.rrd4j.core.RrdDb; import org.rrd4j.core.RrdDef; import org.rrd4j.core.RrdRandomAccessFileBackendFactory; +import org.rrd4j.core.Sample; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +42,7 @@ import com.google.gson.JsonPrimitive; import io.openems.common.OpenemsConstants; +import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; @@ -85,12 +88,14 @@ public Rrd4jTimedataImpl() { @Reference protected ComponentManager componentManager; + protected PersistencePriority persistencePriority = PersistencePriority.MEDIUM; + @Activate void activate(ComponentContext context, Config config) throws Exception { + this.persistencePriority = config.persistencePriority(); super.activate(context, config.id(), config.alias(), config.enabled()); if (config.enabled()) { - this.worker.setNoOfCycles(config.noOfCycles()); this.worker.activate(config.id()); } } @@ -114,7 +119,6 @@ public SortedMap> queryHis long toTimeStamp = toDate.withZoneSameInstant(ZoneOffset.UTC).toEpochSecond(); for (ChannelAddress channelAddress : channels) { - Channel channel = this.componentManager.getChannel(channelAddress); database = this.getExistingRrdDb(channel.address()); if (database == null) { @@ -151,7 +155,8 @@ public SortedMap> queryHis table.put(dateTime, tableRow); } } - } catch (IOException | IllegalArgumentException e) { + + } catch (Exception e) { throw new OpenemsException("Unable to read historic data: " + e.getMessage()); } finally { if (database != null && !database.isClosed()) { @@ -223,7 +228,9 @@ protected static double[] postProcessData(FetchRequest request, int resolution) int split = (int) (step / resolution); for (int i = 1; i < input.length; i++) { for (int j = 0; j < split; j++) { - result[(i - 1) * split + j] = input[i]; + if ((i - 1) * split + j < result.length) { + result[(i - 1) * split + j] = input[i]; + } } } @@ -248,6 +255,7 @@ public SortedMap queryHistoricEnergy(String edgeId, for (ChannelAddress channelAddress : channels) { Channel channel = this.componentManager.getChannel(channelAddress); database = this.getExistingRrdDb(channel.address()); + if (database == null) { continue; // not existing -> abort } @@ -279,7 +287,8 @@ public SortedMap queryHistoricEnergy(String edgeId, } } - } catch (IOException | IllegalArgumentException e) { + + } catch (Exception e) { throw new OpenemsException("Unable to read historic data: " + e.getMessage()); } finally { if (database != null && !database.isClosed()) { @@ -342,7 +351,7 @@ public CompletableFuture> getLatestValue(ChannelAddress channel } try { result.complete(Optional.of(database.getLastDatasourceValues()[0])); - } catch (IOException | ArrayIndexOutOfBoundsException e) { + } catch (Exception e) { result.complete(Optional.empty()); } finally { try { @@ -373,40 +382,51 @@ protected synchronized RrdDb getRrdDb(ChannelAddress channelAddress, Unit channe throws IOException, URISyntaxException { RrdDb rrdDb = this.getExistingRrdDb(channelAddress); if (rrdDb != null) { - /* - * Open existing DB - */ + // Database exists + + // Update database defintion if required + rrdDb = this.updateRrdDbToLatestDefinition(rrdDb, channelAddress, channelUnit); + return rrdDb; } else { - /* - * Create new DB - */ - ChannelDef channelDef = this.getDsDefForChannel(channelUnit); - RrdDef rrdDef = new RrdDef(// - this.getDbFile(channelAddress).toURI(), // - startTime, // Start-Time - DEFAULT_STEP_SECONDS // Step in [s], default: 60 = 1 minute - ); - rrdDef.addDatasource(// - new DsDef(DEFAULT_DATASOURCE_NAME, // - channelDef.dsType, // - DEFAULT_HEARTBEAT_SECONDS, // Heartbeat in [s], default 60 = 1 minute - channelDef.minValue, channelDef.maxValue)); - // detailed recordings - rrdDef.addArchive(channelDef.consolFun, 0.5, 1, 1_440); // 1 step (1 minute), 1440 rows (1 day) - rrdDef.addArchive(channelDef.consolFun, 0.5, 5, 2_880); // 5 steps (5 minutes), 2880 rows (10 days) - // hourly values for a very long time - rrdDef.addArchive(channelDef.consolFun, 0.5, 60, 87_600); // 60 steps (1 hour), 87600 rows (10 years) - - return RrdDb.getBuilder() // - .setBackendFactory(this.factory) // - .usePool() // - .setRrdDef(rrdDef) // - .build(); + // Create new database + return this.createNewDb(channelAddress, channelUnit, startTime); } } + /** + * Creates new DB + * + * @param channelAddress the {@link ChannelAddress} + * @param channelUnit the {@link Unit} of the Channel + * @param startTime the timestamp of the newly added data + * @throws IOException on error + */ + private synchronized RrdDb createNewDb(ChannelAddress channelAddress, Unit channelUnit, long startTime) + throws IOException { + ChannelDef channelDef = this.getDsDefForChannel(channelUnit); + RrdDef rrdDef = new RrdDef(// + this.getDbFile(channelAddress).toURI(), // + startTime, // Start-Time + DEFAULT_STEP_SECONDS // Step in [s], default: 60 = 1 minute + ); + rrdDef.addDatasource(// + new DsDef(DEFAULT_DATASOURCE_NAME, // + channelDef.dsType, // + DEFAULT_HEARTBEAT_SECONDS, // Heartbeat in [s], default 300 = 5 minutes + channelDef.minValue, channelDef.maxValue)); + // detailed recordings + rrdDef.addArchive(channelDef.consolFun, 0.5, 1, 8_928); // 1 step (5 minutes), 8928 rows (31 days) + rrdDef.addArchive(channelDef.consolFun, 0.5, 12, 8_016); // 12 steps (60 minutes), 8016 rows (334 days) + + return RrdDb.getBuilder() // + .setBackendFactory(this.factory) // + .usePool() // + .setRrdDef(rrdDef) // + .build(); + } + /** * Gets an existing RrdDb. * @@ -500,9 +520,9 @@ private ChannelDef getDsDefForChannel(Unit channelUnit) { case THOUSANDTH: return new ChannelDef(DsType.GAUGE, Double.NaN, Double.NaN, ConsolFun.AVERAGE); case PERCENT: - return new ChannelDef(DsType.GAUGE, Double.NaN, 100, ConsolFun.AVERAGE); + return new ChannelDef(DsType.GAUGE, 0, 100, ConsolFun.AVERAGE); case ON_OFF: - return new ChannelDef(DsType.GAUGE, Double.NaN, 1, ConsolFun.AVERAGE); + return new ChannelDef(DsType.GAUGE, 0, 1, ConsolFun.AVERAGE); case CUMULATED_SECONDS: case WATT_HOURS: case KILOWATT_HOURS: @@ -514,6 +534,57 @@ private ChannelDef getDsDefForChannel(Unit channelUnit) { throw new IllegalArgumentException("Unhandled Channel unit [" + channelUnit + "]"); } + /** + * Migrates between different versions of the OpenEMS-RRD4j Definition. + * + * @param database the {@link RrdDb} database + * @param channelAddress the {@link ChannelAddress} + * @param channelUnit the {@link Unit} of the Channel + * @return new {@link RrdDb} + * @throws IOException on error + */ + private RrdDb updateRrdDbToLatestDefinition(RrdDb oldDb, ChannelAddress channelAddress, Unit channelUnit) + throws IOException { + if (oldDb.getArcCount() > 2) { + /* + * This is OpenEMS-RRD4j Definition v1; migrate to v2 + */ + // Read data of last month + long lastTimestamp = oldDb.getLastUpdateTime(); + long firstTimestamp = lastTimestamp - (60 /* minute */ * 60 /* hour */ * 24 /* day */ * 31 /* month */); + FetchRequest fetchRequest = oldDb.createFetchRequest(oldDb.getArchive(0).getConsolFun(), firstTimestamp, + lastTimestamp); + FetchData fetchData = fetchRequest.fetchData(); + double[] values = postProcessData(fetchRequest, DEFAULT_HEARTBEAT_SECONDS); + if (fetchData.getTimestamps().length > 0) { + firstTimestamp = fetchData.getTimestamps()[0]; + } + oldDb.close(); + + // Delete old file + Files.delete(Paths.get(oldDb.getCanonicalPath())); + + // Create new database + RrdDb newDb = this.createNewDb(channelAddress, channelUnit, firstTimestamp - 1); + + // Migrate data + Sample sample = newDb.createSample(); + for (int i = 0; i < values.length; i++) { + sample.setTime(firstTimestamp + i * DEFAULT_HEARTBEAT_SECONDS); + sample.setValue(0, values[i]); + sample.update(); + } + + this.logInfo(this.log, + "Migrate RRD4j Database [" + channelAddress.toString() + "] to OpenEMS Definition v2"); + return newDb; + + } else { + // No Update required + return oldDb; + } + } + @Override protected void logInfo(Logger log, String message) { super.logInfo(log, message); diff --git a/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java b/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java index 2d8f08f5f16..4a973109038 100644 --- a/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java +++ b/io.openems.shared.influxdb/src/io/openems/shared/influxdb/InfluxConnector.java @@ -9,6 +9,10 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -39,11 +43,17 @@ public class InfluxConnector { public static final String MEASUREMENT = "data"; - private static final Logger log = LoggerFactory.getLogger(InfluxConnector.class); + private static final Logger LOG = LoggerFactory.getLogger(InfluxConnector.class); private static final int CONNECT_TIMEOUT = 10; // [s] private static final int READ_TIMEOUT = 10; // [s] private static final int WRITE_TIMEOUT = 10; // [s] + private static final int EXECUTOR_MIN_THREADS = 1; + private static final int EXECUTOR_MAX_THREADS = 50; + private static final int EXECUTOR_QUEUE_SIZE = 100; + + private final Logger log = LoggerFactory.getLogger(InfluxConnector.class); + private final String ip; private final int port; private final String username; @@ -52,6 +62,9 @@ public class InfluxConnector { private final String retentionPolicy; private final boolean isReadOnly; private final BiConsumer, Throwable> onWriteError; + private final ThreadPoolExecutor executor = new ThreadPoolExecutor(EXECUTOR_MIN_THREADS, EXECUTOR_MAX_THREADS, 60L, + TimeUnit.SECONDS, new ArrayBlockingQueue<>(EXECUTOR_QUEUE_SIZE), new ThreadPoolExecutor.DiscardPolicy()); + private final ScheduledExecutorService debugLogExecutor = Executors.newSingleThreadScheduledExecutor(); /** * The Constructor. @@ -78,6 +91,15 @@ public InfluxConnector(String ip, int port, String username, String password, St this.retentionPolicy = retentionPolicy; this.isReadOnly = isReadOnly; this.onWriteError = onWriteError; + this.debugLogExecutor.scheduleWithFixedDelay(() -> { + int queueSize = this.executor.getQueue().size(); + this.log.info(String.format("[monitor] Pool: %d, Active: %d, Pending: %d, Completed: %d %s", + this.executor.getPoolSize(), // + this.executor.getActiveCount(), // + this.executor.getQueue().size(), // + this.executor.getCompletedTaskCount(), // + (queueSize == EXECUTOR_QUEUE_SIZE) ? "!!!BACKPRESSURE!!!" : "")); // + }, 10, 10, TimeUnit.SECONDS); } private InfluxDB _influxDB = null; @@ -107,7 +129,11 @@ private InfluxDB getConnection() { influxDB.setDatabase(this.database); influxDB.setRetentionPolicy(this.retentionPolicy); influxDB.enableBatch(BatchOptions.DEFAULTS // - .jitterDuration(500) // + .precision(TimeUnit.MILLISECONDS) // + .flushDuration(1_000 /* milliseconds */) // + .jitterDuration(1_000 /* milliseconds */) // + .actions(1_000 /* entries */) // + .bufferLimit(1_000_000 /* entries */) // .exceptionHandler(this.onWriteError)); this._influxDB = influxDB; } @@ -115,6 +141,20 @@ private InfluxDB getConnection() { } public void deactivate() { + // Shutdown executor + if (this.executor != null) { + try { + this.executor.shutdown(); + this.executor.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + this.log.warn("tasks interrupted"); + } finally { + if (!this.executor.isTerminated()) { + this.log.warn("cancel non-finished tasks"); + } + this.executor.shutdownNow(); + } + } if (this._influxDB != null) { this._influxDB.close(); } @@ -196,6 +236,11 @@ public QueryResult executeQuery(String query) throws OpenemsException { */ public SortedMap queryHistoricEnergy(Optional influxEdgeId, ZonedDateTime fromDate, ZonedDateTime toDate, Set channels) throws OpenemsNamedException { + if (Math.random() * 4 < this.queryLimit.getLimit()) { + throw new OpenemsException("InfluxDB read is temporarily blocked for Energy values [" + this.queryLimit + + "]. Edge [" + influxEdgeId + "] FromDate [" + fromDate + "] ToDate [" + toDate + "]"); + } + // handle empty call if (channels.isEmpty()) { return new TreeMap(); @@ -406,7 +451,7 @@ private static SortedMap convertHistoricEnergyResul Number number = (Number) valueObj; if (number.intValue() < 0) { // do not consider negative values - log.warn("Got negative Energy value [" + number + "] for query: " + query); + LOG.warn("Got negative Energy value [" + number + "] for query: " + query); value = JsonNull.INSTANCE; } else { value = new JsonPrimitive(number); @@ -431,7 +476,7 @@ private static SortedMap convertHistoricEnergyResul } } if (areAllValuesNull) { - throw new OpenemsException("Energy values are not available"); + throw new OpenemsException("Energy values are not available for query: " + query); } } @@ -478,7 +523,9 @@ public void write(Point point) throws OpenemsException { return; } try { - this.getConnection().write(point); + this.executor.execute(() -> { + this.getConnection().write(point); + }); } catch (InfluxDBIOException e) { throw new OpenemsException("Unable to write point: " + e.getMessage()); } diff --git a/ui/package-lock.json b/ui/package-lock.json index 9a66636b75d..4789c13aa5c 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "openems-ui", - "version": "2021.4.0", + "version": "2021.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/ui/package.json b/ui/package.json index 9702a6f10af..2bab6ca54ce 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "openems-ui", - "version": "2021.4.0", + "version": "2021.5.0", "author": "OpenEMS Association e.V.", "homepage": "http://openems.io", "scripts": { diff --git a/ui/src/app/about/about.component.html b/ui/src/app/about/about.component.html index bda39c0d3fc..3ce0b2a59cd 100644 --- a/ui/src/app/about/about.component.html +++ b/ui/src/app/about/about.component.html @@ -19,8 +19,8 @@ About.openEMS
  • - - About.build: 2021.4.0 (2021-02-26) + + About.build: 2021.5.0 (2021-03-11)
  • diff --git a/ui/src/app/edge/history/energy/energy.component.ts b/ui/src/app/edge/history/energy/energy.component.ts index 8d422158d03..d13a8e042d3 100644 --- a/ui/src/app/edge/history/energy/energy.component.ts +++ b/ui/src/app/edge/history/energy/energy.component.ts @@ -408,6 +408,8 @@ export class EnergyComponent extends AbstractHistoryChart implements OnChanges { directConsumptionData = directConsumption.map(value => { if (value == null) { return null + } else if (value < 0) { + return 0 } else { return value / 1000; // convert to kWh } diff --git a/ui/src/app/edge/settings/profile/profile.component.html b/ui/src/app/edge/settings/profile/profile.component.html index c9dec06f205..c4dbf17bf09 100644 --- a/ui/src/app/edge/settings/profile/profile.component.html +++ b/ui/src/app/edge/settings/profile/profile.component.html @@ -49,65 +49,94 @@ - - - - - - - {{ category.category.title }} - - - - - - - -

    {{ item.alias }} - ({{ item.id }}) - (General.componentInactive) - - -

    - - -

    - - - - - {{ factory.name }} ({{ factory.description }}) -

    -
    + + + + + + + {{ category.category.title }} + + + + + + + +

    {{ item.alias }} + ({{ item.id }}) + (General.componentInactive) + + +

    - -
      - -
    • - {{ p.key }}: {{ p.value }} -
    • -
      -
    - - - - Download Protocol - - - Download Protocol - - - Download Protocol - - + +

    + + + + + {{ factory.name }} ({{ factory.description }}) +

    +
    +
    + +
      + +
    • + {{ p.key }}: {{ p.value }} +
    • +
    + + + + Download Protocol + General.manual + + + Download Protocol + General.manual + + + Download Protocol + General.manual + + + General.manual + + + General.manual + + + + General.manual + + + General.manual + + -
    -
    -
    -
    + + + + + @@ -115,4 +144,4 @@

    {{ item.alias }} - \ No newline at end of file +