of(A a, B b) {
+ return new Tuple<>(a, b);
+ }
+
+}
diff --git a/io.openems.common/src/io/openems/common/utils/JsonUtils.java b/io.openems.common/src/io/openems/common/utils/JsonUtils.java
index 591a960f80f..1ead2112db3 100644
--- a/io.openems.common/src/io/openems/common/utils/JsonUtils.java
+++ b/io.openems.common/src/io/openems/common/utils/JsonUtils.java
@@ -3,6 +3,7 @@
import static io.openems.common.utils.EnumUtils.toEnum;
import java.net.Inet4Address;
+import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -37,7 +38,11 @@
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.types.OpenemsType;
-public class JsonUtils {
+public final class JsonUtils {
+
+ private JsonUtils() {
+ }
+
/**
* Provide a easy way to generate a JsonArray from a list using the given
* convert function to add each element.
@@ -291,6 +296,24 @@ public JsonObjectBuilder addProperty(String property, ZonedDateTime value) {
return this;
}
+ /**
+ * Add a {@link LocalDateTime} value to the {@link JsonObject}.
+ *
+ *
+ * The value gets added in the format of
+ * {@link DateTimeFormatter#ISO_LOCAL_DATE_TIME}.
+ *
+ * @param property the key
+ * @param value the value
+ * @return the {@link JsonObjectBuilder}
+ */
+ public JsonObjectBuilder addProperty(String property, LocalDateTime value) {
+ if (value != null) {
+ this.j.addProperty(property, value.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
+ }
+ return this;
+ }
+
/**
* Add a {@link Boolean} value to the {@link JsonObject}.
*
@@ -1723,6 +1746,32 @@ public static ZonedDateTime getAsZonedDateWithZeroTime(JsonElement element, Stri
}
}
+ /**
+ * Takes a JSON in the form '2020-01-01T00:00:00' and converts it to a
+ * {@link LocalDateTime}.
+ *
+ * @param jElement the {@link JsonElement}
+ * @param memberName the name of the member of the JsonObject
+ * @return the {@link ZonedDateTime}
+ */
+ public static LocalDateTime getAsLocalDateTime(JsonElement jElement, String memberName)
+ throws OpenemsNamedException {
+ return DateUtils.parseLocalDateTimeOrError(toString(toPrimitive(toSubElement(jElement, memberName))));
+ }
+
+ /**
+ * Takes a JSON in the form '2020-01-01T00:00:00Z' and converts it to a
+ * {@link ZonedDateTime}.
+ *
+ * @param jElement the {@link JsonElement}
+ * @param memberName the name of the member of the JsonObject
+ * @return the {@link ZonedDateTime}
+ */
+ public static ZonedDateTime getAsZonedDateTime(JsonElement jElement, String memberName)
+ throws OpenemsNamedException {
+ return DateUtils.parseZonedDateTimeOrError(toString(toPrimitive(toSubElement(jElement, memberName))));
+ }
+
/**
* Takes a JSON in the form 'YYYY-MM-DD' and converts it to a
* {@link ZonedDateTime}.
diff --git a/io.openems.common/src/io/openems/common/utils/ReflectionUtils.java b/io.openems.common/src/io/openems/common/utils/ReflectionUtils.java
index fc88d47a09c..ef924855595 100644
--- a/io.openems.common/src/io/openems/common/utils/ReflectionUtils.java
+++ b/io.openems.common/src/io/openems/common/utils/ReflectionUtils.java
@@ -1,21 +1,134 @@
package io.openems.common.utils;
-import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import io.openems.common.function.ThrowingRunnable;
+import io.openems.common.function.ThrowingSupplier;
public class ReflectionUtils {
+ public static class ReflectionException extends RuntimeException {
+ private static final long serialVersionUID = -8001364348945297741L;
+
+ protected static ReflectionException from(Exception e) {
+ return new ReflectionException(e.getClass().getSimpleName() + ": " + e.getMessage());
+ }
+
+ public ReflectionException(String message) {
+ super(message);
+ }
+ }
+
private ReflectionUtils() {
// no instance needed
}
+ protected static void callGuarded(ThrowingRunnable runnable) throws ReflectionException {
+ try {
+ runnable.run();
+ } catch (Exception e) {
+ throw ReflectionException.from(e);
+ }
+ }
+
+ protected static T callGuarded(ThrowingSupplier supplier) throws ReflectionException {
+ try {
+ return supplier.get();
+ } catch (Exception e) {
+ throw ReflectionException.from(e);
+ }
+ }
+
+ /**
+ * Sets the value of a Field via Java Reflection.
+ *
+ * @param object the target object
+ * @param memberName the name the declared field
+ * @param value the value to be set
+ * @throws Exception on error
+ */
+ public static void setAttributeViaReflection(Object object, String memberName, Object value)
+ throws ReflectionException {
+ var field = getField(object.getClass(), memberName);
+ callGuarded(() -> field.set(object, value));
+ }
+
+ /**
+ * Sets the value of a static Field via Java Reflection.
+ *
+ * @param clazz the {@link Class}
+ * @param memberName the name the declared field
+ * @param value the value to be set
+ * @throws Exception on error
+ */
+ public static void setStaticAttributeViaReflection(Class> clazz, String memberName, Object value)
+ throws ReflectionException {
+ var field = getField(clazz, memberName);
+ callGuarded(() -> field.set(null, value));
+ }
+
+ /**
+ * Gets the value of a Field via Java Reflection.
+ *
+ * @param the type of the value
+ * @param object the target object
+ * @param memberName the name the declared field
+ * @return the value
+ * @throws Exception on error
+ */
@SuppressWarnings("unchecked")
- public static boolean setAttribute(Class extends T> clazz, T object, String memberName, Object value)
- throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+ public static T getValueViaReflection(Object object, String memberName) throws ReflectionException {
+ var field = getField(object.getClass(), memberName);
+ return (T) callGuarded(() -> field.get(object));
+ }
+
+ /**
+ * Invokes a {@link Method} that takes no arguments via Java Reflection.
+ *
+ * @param the type of the result
+ * @param object the target object
+ * @param memberName the name of the method
+ * @return the result of the method
+ * @throws Exception on error
+ */
+ public static T invokeMethodWithoutArgumentsViaReflection(Object object, String memberName)
+ throws ReflectionException {
+ var method = callGuarded(() -> object.getClass().getDeclaredMethod(memberName));
+ return invokeMethodViaReflection(object, method);
+ }
+
+ /**
+ * Invokes a {@link Method} via Java Reflection.
+ *
+ * @param the type of the result
+ * @param object the target object
+ * @param method the {@link Method}
+ * @param args the arguments to be set
+ * @return the result of the method
+ * @throws Exception on error
+ */
+ @SuppressWarnings("unchecked")
+ public static T invokeMethodViaReflection(Object object, Method method, Object... args)
+ throws ReflectionException {
+ method.setAccessible(true);
+ return (T) callGuarded(() -> method.invoke(object, args));
+ }
+
+ /**
+ * Gets the {@link Class#getDeclaredField(String)} in the given {@link Class} or
+ * any of its superclasses.
+ *
+ * @param clazz the given {@link Class}
+ * @param memberName the name of the declared field
+ * @return a {@link Field}
+ * @throws ReflectionException if there is no such field
+ */
+ public static Field getField(Class> clazz, String memberName) throws ReflectionException {
try {
var field = clazz.getDeclaredField(memberName);
field.setAccessible(true);
- field.set(object, value);
- return true;
+ return field;
} catch (NoSuchFieldException e) {
// Ignore.
}
@@ -23,9 +136,8 @@ public static boolean setAttribute(Class extends T> clazz, T object, Strin
// classes.
Class> parent = clazz.getSuperclass();
if (parent == null) {
- return false; // reached 'java.lang.Object'
+ throw new ReflectionException("Reached java.lang.Object");
}
- return setAttribute((Class) parent, object, memberName, value);
+ return getField(parent, memberName);
}
-
}
diff --git a/io.openems.common/src/io/openems/common/utils/XmlUtils.java b/io.openems.common/src/io/openems/common/utils/XmlUtils.java
index b921670e906..0a81ca749b8 100644
--- a/io.openems.common/src/io/openems/common/utils/XmlUtils.java
+++ b/io.openems.common/src/io/openems/common/utils/XmlUtils.java
@@ -1,5 +1,7 @@
package io.openems.common.utils;
+import java.io.IOException;
+import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -7,9 +9,15 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
import io.openems.common.exceptions.OpenemsError;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
@@ -254,4 +262,26 @@ public static Stream stream(final Node node) {
var childNodes = node.getChildNodes();
return IntStream.range(0, childNodes.getLength()).boxed().map(childNodes::item);
}
+
+ /**
+ * Parses the provided XML string and returns the root {@link Element} of the
+ * XML document.
+ *
+ * @param xml the XML string to parse
+ * @return the root {@link Element} of the parsed XML document
+ * @throws ParserConfigurationException if a DocumentBuilder cannot be created
+ * which satisfies the configuration
+ * requested
+ * @throws SAXException if any parse errors occur while
+ * processing the XML
+ * @throws IOException if an I/O error occurs during parsing
+ */
+ public static Element getXmlRootDocument(String xml)
+ throws ParserConfigurationException, SAXException, IOException {
+ var dbFactory = DocumentBuilderFactory.newInstance();
+ var dBuilder = dbFactory.newDocumentBuilder();
+ var is = new InputSource(new StringReader(xml));
+ var doc = dBuilder.parse(is);
+ return doc.getDocumentElement();
+ }
}
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 c76b34dd560..f313c175896 100644
--- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocket.java
+++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocket.java
@@ -128,11 +128,17 @@ protected final boolean sendMessage(WebSocket ws, JsonrpcMessage message) {
}
private void sendMessageFailedLog(WebSocket ws, JsonrpcMessage message) {
- this.logWarn(this.log, new StringBuilder() //
- .append("[").append(generateWsDataString(ws)) //
- .append("] Unable to send message: Connection is closed. ") //
- .append(toShortString(simplifyJsonrpcMessage(message), 100)) //
- .toString());
+ final var b = new StringBuilder();
+
+ var wsDataString = generateWsDataString(ws);
+ if (!wsDataString.isEmpty()) {
+ b.append("[").append(generateWsDataString(ws)).append("] ");
+ }
+
+ this.logWarn(this.log, //
+ b.append("Unable to send message: Connection is closed. ") //
+ .append(toShortString(simplifyJsonrpcMessage(message), 200)) //
+ .toString());
}
/**
diff --git a/io.openems.common/src/io/openems/common/websocket/DummyWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/DummyWebsocketServer.java
index b815deeb71c..f4b2b6cf916 100644
--- a/io.openems.common/src/io/openems/common/websocket/DummyWebsocketServer.java
+++ b/io.openems.common/src/io/openems/common/websocket/DummyWebsocketServer.java
@@ -93,12 +93,7 @@ private DummyWebsocketServer(DummyWebsocketServer.Builder builder) {
@Override
protected WsData createWsData(WebSocket ws) {
- return new WsData(ws) {
- @Override
- public String toString() {
- return "DummyWebsocketServer.WsData []";
- }
- };
+ return new WsData(ws);
}
@Override
diff --git a/io.openems.common/src/io/openems/common/websocket/MyDraft6455.java b/io.openems.common/src/io/openems/common/websocket/MyDraft6455.java
index 3f549c9cb49..4c4968114c6 100644
--- a/io.openems.common/src/io/openems/common/websocket/MyDraft6455.java
+++ b/io.openems.common/src/io/openems/common/websocket/MyDraft6455.java
@@ -23,6 +23,7 @@
import org.java_websocket.enums.Opcode;
import org.java_websocket.enums.ReadyState;
import org.java_websocket.enums.Role;
+import org.java_websocket.exceptions.IncompleteException;
import org.java_websocket.exceptions.InvalidDataException;
import org.java_websocket.exceptions.InvalidFrameException;
import org.java_websocket.exceptions.InvalidHandshakeException;
diff --git a/io.openems.common/src/io/openems/common/websocket/WebsocketUtils.java b/io.openems.common/src/io/openems/common/websocket/WebsocketUtils.java
index e9f51ae5b04..cb80dc12b69 100644
--- a/io.openems.common/src/io/openems/common/websocket/WebsocketUtils.java
+++ b/io.openems.common/src/io/openems/common/websocket/WebsocketUtils.java
@@ -53,11 +53,11 @@ public static String parseRemoteIdentifier(WebSocket ws, Handshakedata handshake
}
/**
- * Gets the toString() content of the WsData attachment of the WebSocket; or
+ * Gets the toLogString() content of the WsData attachment of the WebSocket; or
* empty string if not available.
*
* @param ws the WebSocket
- * @return the {@link WsData#toString()} content
+ * @return the {@link WsData#toLogString()} content
*/
public static String generateWsDataString(WebSocket ws) {
if (ws == null) {
@@ -67,6 +67,10 @@ public static String generateWsDataString(WebSocket ws) {
if (wsData == null) {
return "";
}
- return wsData.toString();
+ var logString = wsData.toLogString();
+ if (logString == null) {
+ return "";
+ }
+ return logString;
}
}
diff --git a/io.openems.common/src/io/openems/common/websocket/WsData.java b/io.openems.common/src/io/openems/common/websocket/WsData.java
index b43aedad7f7..852b4290125 100644
--- a/io.openems.common/src/io/openems/common/websocket/WsData.java
+++ b/io.openems.common/src/io/openems/common/websocket/WsData.java
@@ -21,14 +21,14 @@
* Objects of this class are used to store additional data with websocket
* connections of WebSocketClient and WebSocketServer.
*/
-public abstract class WsData {
+public class WsData {
/**
* Holds the WebSocket.
*/
private final WebSocket websocket;
- protected WsData(WebSocket ws) {
+ public WsData(WebSocket ws) {
this.websocket = ws;
}
@@ -138,10 +138,11 @@ public void handleJsonrpcResponse(JsonrpcResponse response) throws OpenemsNamedE
}
/**
- * Provides a specific toString method.
+ * Provides a specific log string.
*
* @return a specific string for this instance
*/
- @Override
- public abstract String toString();
+ protected String toLogString() {
+ return "";
+ }
}
diff --git a/io.openems.common/src/io/openems/common/worker/AbstractWorker.java b/io.openems.common/src/io/openems/common/worker/AbstractWorker.java
index 29b371fd99b..c78b265fb2b 100644
--- a/io.openems.common/src/io/openems/common/worker/AbstractWorker.java
+++ b/io.openems.common/src/io/openems/common/worker/AbstractWorker.java
@@ -65,9 +65,7 @@ public void activate(String name) {
* false
*/
public void modified(String name, boolean initiallyTriggerNextRun) {
- if (!this.thread.isAlive() && !this.thread.isInterrupted() && !this.isStopped.get()) {
- this.startWorker(name, initiallyTriggerNextRun);
- }
+ this.startWorker(name, initiallyTriggerNextRun);
}
/**
@@ -79,12 +77,13 @@ public void modified(String name) {
this.modified(name, true);
}
- private void startWorker(String name, boolean autoTriggerNextRun) {
+ private synchronized void startWorker(String name, boolean autoTriggerNextRun) {
if (name != null) {
this.thread.setName(name);
}
- this.thread.start();
-
+ if (!this.thread.isAlive() && !this.thread.isInterrupted() && !this.isStopped.get()) {
+ this.thread.start();
+ }
if (autoTriggerNextRun) {
this.triggerNextRun();
}
diff --git a/io.openems.common/test/io/openems/common/jscalendar/JSCalendarTest.java b/io.openems.common/test/io/openems/common/jscalendar/JSCalendarTest.java
new file mode 100644
index 00000000000..73061feb84a
--- /dev/null
+++ b/io.openems.common/test/io/openems/common/jscalendar/JSCalendarTest.java
@@ -0,0 +1,136 @@
+package io.openems.common.jscalendar;
+
+import static io.openems.common.jscalendar.JSCalendar.RecurrenceFrequency.WEEKLY;
+import static io.openems.common.test.TestUtils.createDummyClock;
+import static io.openems.common.utils.JsonUtils.buildJsonObject;
+import static io.openems.common.utils.JsonUtils.prettyToString;
+import static io.openems.common.utils.UuidUtils.getNilUuid;
+import static java.time.DayOfWeek.FRIDAY;
+import static java.time.DayOfWeek.MONDAY;
+import static java.time.DayOfWeek.SATURDAY;
+import static java.time.DayOfWeek.SUNDAY;
+import static java.time.DayOfWeek.THURSDAY;
+import static java.time.DayOfWeek.TUESDAY;
+import static java.time.DayOfWeek.WEDNESDAY;
+import static java.util.function.Function.identity;
+import static org.junit.Assert.assertEquals;
+
+import java.time.ZonedDateTime;
+
+import org.junit.Test;
+
+import com.google.gson.JsonObject;
+
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
+import io.openems.common.utils.JsonUtils;
+
+//CHECKSTYLE:OFF
+public class JSCalendarTest {
+ // CHECKSTYLE:ON
+
+ @Test
+ public void testWeekday() throws OpenemsNamedException {
+ var clock = createDummyClock();
+ var sut = new JSCalendar.Task.Builder(getNilUuid(), ZonedDateTime.now(clock)) //
+ .setStart("2024-06-17T07:00:00") //
+ .addRecurrenceRule(b -> b //
+ .setFrequency(WEEKLY) //
+ .addByDay(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY)) //
+ .setPayload(buildJsonObject() //
+ .addProperty("sessionEnergy", 10000) //
+ .build()) //
+ .build();
+ assertEquals("""
+ {
+ "@type": "Task",
+ "uid": "00000000-0000-0000-0000-000000000000",
+ "updated": "2020-01-01T00:00:00Z",
+ "start": "2024-06-17T07:00:00",
+ "recurrenceRules": [
+ {
+ "frequency": "weekly",
+ "byDay": [
+ "mo",
+ "tu",
+ "we",
+ "th",
+ "fr"
+ ]
+ }
+ ],
+ "payload": {
+ "sessionEnergy": 10000
+ }
+ }""", prettyToString(sut.toJson(identity())));
+
+ var next = sut.getNextOccurence(ZonedDateTime.now(clock));
+ assertEquals("2024-06-17T07:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-06-18T07:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-06-19T07:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-06-20T07:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-06-21T07:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1)); // next week
+ assertEquals("2024-06-24T07:00Z", next.toString());
+ next = sut.getNextOccurence(next);
+ assertEquals("2024-06-24T07:00Z", next.toString()); // same
+
+ // Parse JSON
+ var fromJson = JSCalendar.Task.fromJson(JsonUtils.buildJsonArray() //
+ .add(sut.toJson(identity())) //
+ .build(), j -> j);
+ assertEquals(sut.toJson(identity()), fromJson.get(0).toJson(identity()));
+ }
+
+ @Test
+ public void testWeekend() throws OpenemsNamedException {
+ var clock = createDummyClock();
+ var sut = new JSCalendar.Task.Builder(getNilUuid(), ZonedDateTime.now(clock)) //
+ .setStart("2024-06-17T00:00:00") //
+ .addRecurrenceRule(b -> b //
+ .setFrequency(WEEKLY) //
+ .addByDay(SATURDAY, SUNDAY)) //
+ .setPayload(buildJsonObject() //
+ .addProperty("sessionEnergy", 10001) //
+ .build()) //
+ .build();
+ assertEquals("""
+ {
+ "@type": "Task",
+ "uid": "00000000-0000-0000-0000-000000000000",
+ "updated": "2020-01-01T00:00:00Z",
+ "start": "2024-06-17T00:00:00",
+ "recurrenceRules": [
+ {
+ "frequency": "weekly",
+ "byDay": [
+ "sa",
+ "su"
+ ]
+ }
+ ],
+ "payload": {
+ "sessionEnergy": 10001
+ }
+ }""", prettyToString(sut.toJson(identity())));
+
+ var next = sut.getNextOccurence(ZonedDateTime.now(clock));
+ assertEquals("2024-06-22T00:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-06-23T00:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-06-29T00:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-06-30T00:00Z", next.toString());
+ next = sut.getNextOccurence(next.plusSeconds(1));
+ assertEquals("2024-07-06T00:00Z", next.toString());
+
+ // Parse JSON
+ var fromJson = JSCalendar.Task.fromJson(sut.toJson(identity()), identity());
+ assertEquals(sut.toJson(identity()), fromJson.toJson(identity()));
+ }
+
+}
diff --git a/io.openems.common/test/io/openems/common/jsonrpc/response/CreateXlxsTest.java b/io.openems.common/test/io/openems/common/jsonrpc/response/CreateXlxsTest.java
new file mode 100644
index 00000000000..f855b102bee
--- /dev/null
+++ b/io.openems.common/test/io/openems/common/jsonrpc/response/CreateXlxsTest.java
@@ -0,0 +1,248 @@
+package io.openems.common.jsonrpc.response;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
+import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesExportXlsxResponse.Channel;
+import io.openems.common.session.Language;
+import io.openems.common.timedata.XlsxExportDetailData;
+import io.openems.common.timedata.XlsxExportDetailData.XlsxExportCategory;
+import io.openems.common.timedata.XlsxExportDetailData.XlsxExportDataEntry;
+import io.openems.common.timedata.XlsxExportDetailData.XlsxExportDataEntry.HistoricTimedataSaveType;
+import io.openems.common.types.ChannelAddress;
+import io.openems.common.types.CurrencyConfig;
+
+public class CreateXlxsTest {
+
+ private static SortedMap getMockedEnergyData() {
+ return ImmutableSortedMap.naturalOrder() //
+ .put(Channel.GRID_BUY_ACTIVE_ENERGY, new JsonPrimitive(500)) //
+ .put(Channel.GRID_SELL_ACTIVE_ENERGY, new JsonPrimitive(0)) //
+ .put(Channel.PRODUCTION_ACTIVE_ENERGY, new JsonPrimitive(300)) //
+ .put(Channel.CONSUMPTION_ACTIVE_ENERGY, new JsonPrimitive(700)) //
+ .put(Channel.ESS_DC_CHARGE_ENERGY, new JsonPrimitive(100)) //
+ .put(Channel.ESS_DC_DISCHARGE_ENERGY, new JsonPrimitive(80)) //
+ .build();
+ }
+
+ private static SortedMap> getMockedPowerData() {
+
+ SortedMap values = new TreeMap<>();
+ values.put(Channel.GRID_ACTIVE_POWER, new JsonPrimitive(50));
+ values.put(Channel.PRODUCTION_ACTIVE_POWER, new JsonPrimitive(50));
+ values.put(Channel.CONSUMPTION_ACTIVE_POWER, new JsonPrimitive(50));
+ values.put(Channel.ESS_DISCHARGE_POWER, new JsonPrimitive(50));
+ values.put(Channel.ESS_SOC, new JsonPrimitive(50));
+ values.put(new ChannelAddress("meter0", "ActivePower"), new JsonPrimitive(100));
+ values.put(new ChannelAddress("meter1", "ActivePower"), new JsonPrimitive(412));
+ values.put(new ChannelAddress("evcs0", "ChargePower"), new JsonPrimitive(75));
+ values.put(new ChannelAddress("meter2", "ActivePower"), new JsonPrimitive(10));
+ values.put(new ChannelAddress("_sum", "GridBuyPower"), new JsonPrimitive(292.5));
+
+ return ImmutableSortedMap.>naturalOrder()
+ .put(ZonedDateTime.of(2020, 07, 01, 0, 15, 0, 0, ZoneId.systemDefault()).plusMinutes(15), values) //
+ .put(ZonedDateTime.of(2020, 07, 01, 0, 30, 0, 0, ZoneId.systemDefault()).plusMinutes(15), values) //
+ .put(ZonedDateTime.of(2020, 07, 01, 0, 45, 0, 0, ZoneId.systemDefault()).plusMinutes(15), values) //
+ .put(ZonedDateTime.of(2020, 07, 01, 1, 0, 0, 0, ZoneId.systemDefault()).plusMinutes(15), values) //
+ .put(ZonedDateTime.of(2020, 07, 01, 1, 15, 0, 0, ZoneId.systemDefault()).plusMinutes(15), values) //
+ .put(ZonedDateTime.of(2020, 07, 01, 1, 30, 0, 0, ZoneId.systemDefault()).plusMinutes(15), values) //
+ .build();
+ }
+
+ private static XlsxExportDetailData getMockedDetailData() {
+ final var enumMap = new EnumMap>(XlsxExportCategory.class);
+ final var consumption = new ArrayList();
+ final var production = new ArrayList();
+ final var tou = new ArrayList();
+
+ enumMap.put(XlsxExportCategory.PRODUCTION, production);
+ enumMap.put(XlsxExportCategory.CONSUMPTION, consumption);
+ enumMap.put(XlsxExportCategory.TIME_OF_USE_TARIFF, tou);
+
+ production.add(new XlsxExportDataEntry("PV-Dach", new ChannelAddress("meter0", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ production.add(new XlsxExportDataEntry("PV-Alm", new ChannelAddress("meter1", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ consumption.add(new XlsxExportDataEntry("Consumption Meter", new ChannelAddress("meter2", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ consumption.add(new XlsxExportDataEntry("Wallbox Garage", new ChannelAddress("evcs0", "ChargePower"),
+ HistoricTimedataSaveType.POWER));
+ tou.add(new XlsxExportDataEntry("Dynamisch Gut", new ChannelAddress("_sum", "GridBuyPower"),
+ HistoricTimedataSaveType.POWER));
+
+ return new XlsxExportDetailData(enumMap, CurrencyConfig.EUR);
+ }
+
+ /**
+ * Main Method for creating a excel export with mocked data.
+ *
+ * @param args not used
+ * @throws IOException if file cant be written
+ * @throws OpenemsNamedException requests fails
+ */
+ public static void main(String[] args) throws IOException, OpenemsNamedException {
+ createFullXlsx();
+ createHalfXlsx();
+ createConsumptionOnlyXlsx();
+ createProductionOnlyXlsx();
+ createTouOnlyXlsx();
+ createProductionAndTouXlsx();
+ createConsumptionAndTouXlsx();
+ createnSingleOfAllXlsx();
+ }
+
+ private static void createFullXlsx() throws IOException, OpenemsNamedException {
+ var fromDate = ZonedDateTime.of(2020, 07, 01, 0, 0, 0, 0, ZoneId.systemDefault());
+ var toDate = ZonedDateTime.of(2020, 07, 02, 0, 0, 0, 0, ZoneId.systemDefault());
+
+ var powerData = CreateXlxsTest.getMockedPowerData();
+ var energyData = CreateXlxsTest.getMockedEnergyData();
+ var detailData = CreateXlxsTest.getMockedDetailData();
+
+ final var request = new QueryHistoricTimeseriesExportXlsxResponse(UUID.randomUUID(), "edge0", fromDate, toDate,
+ powerData, energyData, Language.EN, detailData);
+
+ var payload = request.getPayload();
+
+ byte[] excelData = Base64.getDecoder().decode(payload);
+
+ String filePath = ".\\..\\build\\fullTestPrint.xlsx";
+
+ try (FileOutputStream fos = new FileOutputStream(filePath)) {
+ fos.write(excelData);
+ System.out.println("Testfile created under: " + filePath);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void createHalfXlsx() throws IOException, OpenemsNamedException {
+ createTestPrint(".\\..\\build\\emptyTestPrint.xlsx", null, null, null);
+ }
+
+ private static void createTestPrint(String filePath, Consumer> consProd,
+ Consumer> consCons, Consumer> consTou)
+ throws IOException, OpenemsNamedException {
+ final var enumMap = new EnumMap>(XlsxExportCategory.class);
+ final var consumption = new ArrayList();
+ final var production = new ArrayList();
+ final var tou = new ArrayList();
+
+ if (consProd != null) {
+ consProd.accept(production);
+ }
+
+ if (consCons != null) {
+ consCons.accept(consumption);
+ }
+
+ if (consTou != null) {
+ consTou.accept(tou);
+ }
+
+ enumMap.put(XlsxExportCategory.PRODUCTION, production);
+ enumMap.put(XlsxExportCategory.CONSUMPTION, consumption);
+ enumMap.put(XlsxExportCategory.TIME_OF_USE_TARIFF, tou);
+
+ var detailData = new XlsxExportDetailData(enumMap, CurrencyConfig.EUR);
+
+ var fromDate = ZonedDateTime.of(2020, 07, 01, 0, 0, 0, 0, ZoneId.systemDefault());
+ var toDate = ZonedDateTime.of(2020, 07, 02, 0, 0, 0, 0, ZoneId.systemDefault());
+
+ var powerData = CreateXlxsTest.getMockedPowerData();
+ var energyData = CreateXlxsTest.getMockedEnergyData();
+
+ final var request = new QueryHistoricTimeseriesExportXlsxResponse(UUID.randomUUID(), "edge0", fromDate, toDate,
+ powerData, energyData, Language.EN, detailData);
+
+ var payload = request.getPayload();
+
+ byte[] excelData = Base64.getDecoder().decode(payload);
+
+ try (FileOutputStream fos = new FileOutputStream(filePath)) {
+ fos.write(excelData);
+ System.out.println("Testfile created under: " + filePath);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void createProductionOnlyXlsx() throws IOException, OpenemsNamedException {
+ createTestPrint(".\\..\\build\\prodTestPrint.xlsx", production -> {
+ production.add(new XlsxExportDataEntry("PV-Dach", new ChannelAddress("meter0", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ production.add(new XlsxExportDataEntry("PV-Alm", new ChannelAddress("meter1", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ }, null, null);
+ }
+
+ private static void createConsumptionOnlyXlsx() throws IOException, OpenemsNamedException {
+ createTestPrint(".\\..\\build\\consTestPrint.xlsx", null, consumption -> {
+ consumption.add(new XlsxExportDataEntry("Consumption Meter", new ChannelAddress("meter2", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ consumption.add(new XlsxExportDataEntry("Wallbox Garage", new ChannelAddress("evcs0", "ChargePower"),
+ HistoricTimedataSaveType.POWER));
+ }, null);
+ }
+
+ private static void createTouOnlyXlsx() throws IOException, OpenemsNamedException {
+ createTestPrint(".\\..\\build\\touPrint.xlsx", null, null, tou -> {
+ tou.add(new XlsxExportDataEntry("Dynamisch Gut", new ChannelAddress("_sum", "GridBuyPower"),
+ HistoricTimedataSaveType.POWER));
+ });
+ }
+
+ private static void createProductionAndTouXlsx() throws IOException, OpenemsNamedException {
+ createTestPrint(".\\..\\build\\prodAndTouPrint.xlsx", production -> {
+ production.add(new XlsxExportDataEntry("PV-Dach", new ChannelAddress("meter0", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ production.add(new XlsxExportDataEntry("PV-Alm", new ChannelAddress("meter1", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ }, null, tou -> {
+ tou.add(new XlsxExportDataEntry("Dynamisch Gut", new ChannelAddress("_sum", "GridBuyPower"),
+ HistoricTimedataSaveType.POWER));
+ });
+
+ }
+
+ private static void createConsumptionAndTouXlsx() throws IOException, OpenemsNamedException {
+ createTestPrint(".\\..\\build\\consAndTouPrint.xlsx", null, consumption -> {
+ consumption.add(new XlsxExportDataEntry("Consumption Meter", new ChannelAddress("meter2", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ consumption.add(new XlsxExportDataEntry("Wallbox Garage", new ChannelAddress("evcs0", "ChargePower"),
+ HistoricTimedataSaveType.POWER));
+ }, tou -> {
+ tou.add(new XlsxExportDataEntry("Dynamisch Gut", new ChannelAddress("_sum", "GridBuyPower"),
+ HistoricTimedataSaveType.POWER));
+ });
+ }
+
+ private static void createnSingleOfAllXlsx() throws IOException, OpenemsNamedException {
+ createTestPrint(".\\..\\build\\singleOfAllPrint.xlsx", production -> {
+ production.add(new XlsxExportDataEntry("PV-Alm", new ChannelAddress("meter1", "ActivePower"),
+ HistoricTimedataSaveType.POWER));
+ }, consumption -> {
+ consumption.add(new XlsxExportDataEntry("Wallbox Garage", new ChannelAddress("evcs0", "ChargePower"),
+ HistoricTimedataSaveType.POWER));
+ }, tou -> {
+ tou.add(new XlsxExportDataEntry("Dynamisch Gut", new ChannelAddress("_sum", "GridBuyPower"),
+ HistoricTimedataSaveType.POWER));
+ });
+
+ }
+}
diff --git a/io.openems.common/test/io/openems/common/jsonrpc/response/QueryHistoricTimeseriesExportXlsxResponseTest.java b/io.openems.common/test/io/openems/common/jsonrpc/response/QueryHistoricTimeseriesExportXlsxResponseTest.java
index d59804bff2e..e34f9f1a8a6 100644
--- a/io.openems.common/test/io/openems/common/jsonrpc/response/QueryHistoricTimeseriesExportXlsxResponseTest.java
+++ b/io.openems.common/test/io/openems/common/jsonrpc/response/QueryHistoricTimeseriesExportXlsxResponseTest.java
@@ -70,7 +70,7 @@ private byte[] generateXlsxFile() throws OpenemsNamedException, IOException {
) {
var ws = workbook.newWorksheet("Export");
- Locale currentLocale = new Locale("en", "EN");
+ Locale currentLocale = Locale.of("en", "EN");
var translationBundle = ResourceBundle.getBundle("io.openems.common.jsonrpc.response.translation",
currentLocale);
diff --git a/io.openems.common/test/io/openems/common/timedata/TimeoutTest.java b/io.openems.common/test/io/openems/common/timedata/TimeoutTest.java
new file mode 100644
index 00000000000..15ef3d8b49d
--- /dev/null
+++ b/io.openems.common/test/io/openems/common/timedata/TimeoutTest.java
@@ -0,0 +1,27 @@
+package io.openems.common.timedata;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.time.temporal.ChronoUnit;
+
+import org.junit.Test;
+
+import io.openems.common.test.TimeLeapClock;
+
+public class TimeoutTest {
+
+ @Test
+ public void test() {
+ final var timeout = Timeout.ofSeconds(120);
+ final var timeLeap = new TimeLeapClock();
+ timeout.start(timeLeap);
+
+ timeLeap.leap(20, ChronoUnit.SECONDS);
+ assertFalse(timeout.elapsed(timeLeap));
+
+ timeLeap.leap(121, ChronoUnit.SECONDS);
+ assertTrue(timeout.elapsed(timeLeap));
+ }
+
+}
diff --git a/io.openems.common/test/io/openems/common/timedata/XlsxExportUtilTest.java b/io.openems.common/test/io/openems/common/timedata/XlsxExportUtilTest.java
new file mode 100644
index 00000000000..1328f3a4ff9
--- /dev/null
+++ b/io.openems.common/test/io/openems/common/timedata/XlsxExportUtilTest.java
@@ -0,0 +1,92 @@
+package io.openems.common.timedata;
+
+import static io.openems.common.utils.JsonUtils.toJson;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSortedMap;
+
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
+import io.openems.common.timedata.XlsxExportDetailData.XlsxExportCategory;
+import io.openems.common.timedata.XlsxExportDetailData.XlsxExportDataEntry.HistoricTimedataSaveType;
+import io.openems.common.types.EdgeConfig.ActualEdgeConfig;
+import io.openems.common.types.EdgeConfig.Component;
+import io.openems.common.types.EdgeConfig.Factory;
+import io.openems.common.types.EdgeConfig.Factory.Property;
+
+public class XlsxExportUtilTest {
+
+ @Test
+ public void testGetDetailData() throws OpenemsNamedException {
+ var edgeConfig = ActualEdgeConfig.create() //
+ .addComponent("meter0",
+ new Component("meter0", "My CONSUMPTION_METERED Meter", "Meter.Socomec.Threephase",
+ // Properties
+ ImmutableSortedMap.of("type", toJson("CONSUMPTION_METERED")),
+ // Channels
+ ImmutableSortedMap.of())) //
+ .addComponent("meter1",
+ new Component("meter1", "My CONSUMPTION_NOT_METERED Meter", "Meter.Socomec.Threephase",
+ // Properties
+ ImmutableSortedMap.of("type", toJson("CONSUMPTION_NOT_METERED")),
+ // Channels
+ ImmutableSortedMap.of())) //
+ .addComponent("meter2", new Component("meter2", "My PRODUCTION Meter", "Meter.Socomec.Threephase",
+ // Properties
+ ImmutableSortedMap.of("type", toJson("PRODUCTION")),
+ // Channels
+ ImmutableSortedMap.of())) //
+ .addComponent("meter3",
+ new Component("meter3", "My MANAGED_CONSUMPTION_METERED Meter", "Meter.Socomec.Threephase",
+ // Properties
+ ImmutableSortedMap.of("type", toJson("MANAGED_CONSUMPTION_METERED")),
+ // Channels
+ ImmutableSortedMap.of())) //
+
+ .addFactory("Meter.Socomec.Threephase",
+ new Factory("Meter.Socomec.Threephase", "My Name", "My Description", //
+ new Property[] {}, //
+ // Natures
+ new String[] { "io.openems.edge.meter.api.ElectricityMeter" })) //
+ .buildEdgeConfig();
+
+ final var result = XlsxExportUtil.getDetailData(edgeConfig);
+
+ var consumptions = result.data().get(XlsxExportCategory.CONSUMPTION);
+ assertEquals(3, consumptions.size());
+
+ {
+ var meter = consumptions.get(0);
+ assertEquals("My CONSUMPTION_METERED Meter", meter.alias());
+ assertEquals("meter0/ActivePower", meter.channel().toString());
+ assertEquals(HistoricTimedataSaveType.POWER, meter.type());
+ }
+ {
+ var meter = consumptions.get(1);
+ assertEquals("My CONSUMPTION_NOT_METERED Meter", meter.alias());
+ assertEquals("meter1/ActivePower", meter.channel().toString());
+ assertEquals(HistoricTimedataSaveType.POWER, meter.type());
+ }
+ {
+ var meter = consumptions.get(2);
+ assertEquals("My MANAGED_CONSUMPTION_METERED Meter", meter.alias());
+ assertEquals("meter3/ActivePower", meter.channel().toString());
+ assertEquals(HistoricTimedataSaveType.POWER, meter.type());
+ }
+
+ var productions = result.data().get(XlsxExportCategory.PRODUCTION);
+ assertEquals(1, productions.size());
+
+ {
+ var meter = productions.get(0);
+ assertEquals("My PRODUCTION Meter", meter.alias());
+ assertEquals("meter2/ActivePower", meter.channel().toString());
+ assertEquals(HistoricTimedataSaveType.POWER, meter.type());
+ }
+
+ var touts = result.data().get(XlsxExportCategory.TIME_OF_USE_TARIFF);
+ assertEquals(0, touts.size());
+ }
+
+}
diff --git a/io.openems.common/test/io/openems/common/utils/JsonUtilsTest.java b/io.openems.common/test/io/openems/common/utils/JsonUtilsTest.java
index badcaae75dd..de06b6e2bd3 100644
--- a/io.openems.common/test/io/openems/common/utils/JsonUtilsTest.java
+++ b/io.openems.common/test/io/openems/common/utils/JsonUtilsTest.java
@@ -44,6 +44,7 @@
import static io.openems.common.utils.JsonUtils.getAsJsonArray;
import static io.openems.common.utils.JsonUtils.getAsJsonElement;
import static io.openems.common.utils.JsonUtils.getAsJsonObject;
+import static io.openems.common.utils.JsonUtils.getAsLocalDateTime;
import static io.openems.common.utils.JsonUtils.getAsLong;
import static io.openems.common.utils.JsonUtils.getAsOptionalBoolean;
import static io.openems.common.utils.JsonUtils.getAsOptionalDouble;
@@ -64,6 +65,7 @@
import static io.openems.common.utils.JsonUtils.getAsStringOrElse;
import static io.openems.common.utils.JsonUtils.getAsType;
import static io.openems.common.utils.JsonUtils.getAsUUID;
+import static io.openems.common.utils.JsonUtils.getAsZonedDateTime;
import static io.openems.common.utils.JsonUtils.getAsZonedDateWithZeroTime;
import static io.openems.common.utils.JsonUtils.getOptionalSubElement;
import static io.openems.common.utils.JsonUtils.getSubElement;
@@ -80,11 +82,13 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import java.net.Inet4Address;
import java.net.UnknownHostException;
+import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
@@ -148,6 +152,8 @@ private static final void assertAllThrow(Class expected
.addProperty("Enum3", (Unit) null) //
.addProperty("Inet4Address", "192.168.1.2") //
.addProperty("UUID", "c48e2e28-09be-41d5-8e58-260d162991cc") //
+ .addProperty("ZonedDateTime", ZonedDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC"))) //
+ .addProperty("LocalDateTime", LocalDateTime.of(1900, 1, 1, 0, 0, 0, 0)) //
.addPropertyIfNotNull("Boolean1", (Boolean) null) //
.addPropertyIfNotNull("Boolean2", Boolean.FALSE) //
.addPropertyIfNotNull("Double1", (Double) null) //
@@ -680,6 +686,13 @@ public void testGetAsZonedDateTime() throws OpenemsNamedException {
assertOpenemsError(JSON_NO_DATE_MEMBER, //
() -> getAsZonedDateWithZeroTime(j, "foo", ZoneId.of("UTC")) //
);
+
+ assertEquals("1900-01-01T00:00Z", getAsZonedDateTime(JSON_OBJECT, "ZonedDateTime").toString());
+ }
+
+ @Test
+ public void testGetAsLocalDateTime() throws OpenemsNamedException {
+ assertEquals("1900-01-01T00:00", getAsLocalDateTime(JSON_OBJECT, "LocalDateTime").toString());
}
@Test
@@ -744,6 +757,9 @@ public void testIsEmptyJsonArray() throws OpenemsNamedException {
@Test
public void testGenerateJsonArray() {
+ assertNull(generateJsonArray(null));
+ assertEquals(JsonNull.INSTANCE, generateJsonArray(List.of(JsonNull.INSTANCE)).get(0));
+
var list = List.of("foo", "bar");
var r = generateJsonArray(list, v -> new JsonPrimitive(v));
assertEquals("foo", r.get(0).getAsString());
diff --git a/io.openems.common/test/io/openems/common/websocket/ClientReconnectorWorkerTest.java b/io.openems.common/test/io/openems/common/websocket/ClientReconnectorWorkerTest.java
index 3eb4161bd07..62373bfe85a 100644
--- a/io.openems.common/test/io/openems/common/websocket/ClientReconnectorWorkerTest.java
+++ b/io.openems.common/test/io/openems/common/websocket/ClientReconnectorWorkerTest.java
@@ -9,28 +9,15 @@
public class ClientReconnectorWorkerTest {
- private static class MyWsData extends WsData {
-
- public MyWsData(WebSocket ws) {
- super(ws);
- }
-
- @Override
- public String toString() {
- return "";
- }
-
- }
-
- private static class MyWebsocketClient extends AbstractWebsocketClient {
+ private static class MyWebsocketClient extends AbstractWebsocketClient {
public MyWebsocketClient(String name, URI serverUri) {
super(name, serverUri);
}
@Override
- protected MyWsData createWsData(WebSocket ws) {
- return new MyWsData(ws);
+ protected WsData createWsData(WebSocket ws) {
+ return new WsData(ws);
}
@Override
diff --git a/io.openems.common/test/io/openems/common/websocket/DummyWebsocketServerTest.java b/io.openems.common/test/io/openems/common/websocket/DummyWebsocketServerTest.java
new file mode 100644
index 00000000000..172d3516be5
--- /dev/null
+++ b/io.openems.common/test/io/openems/common/websocket/DummyWebsocketServerTest.java
@@ -0,0 +1,15 @@
+package io.openems.common.websocket;
+
+import org.junit.Test;
+
+public class DummyWebsocketServerTest {
+
+ @Test
+ public void test() {
+ var sut = DummyWebsocketServer.create() //
+ .build();
+ sut.createWsData(null);
+ sut.stop();
+ }
+
+}
diff --git a/io.openems.common/test/io/openems/common/websocket/WebsocketUtilsTest.java b/io.openems.common/test/io/openems/common/websocket/WebsocketUtilsTest.java
new file mode 100644
index 00000000000..25febb897d4
--- /dev/null
+++ b/io.openems.common/test/io/openems/common/websocket/WebsocketUtilsTest.java
@@ -0,0 +1,15 @@
+package io.openems.common.websocket;
+
+import static io.openems.common.websocket.WebsocketUtils.generateWsDataString;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class WebsocketUtilsTest {
+
+ @Test
+ public void test() {
+ assertEquals("", generateWsDataString(null));
+ }
+
+}
diff --git a/io.openems.common/test/io/openems/common/websocket/WsDataTest.java b/io.openems.common/test/io/openems/common/websocket/WsDataTest.java
new file mode 100644
index 00000000000..5dd1a7aad2f
--- /dev/null
+++ b/io.openems.common/test/io/openems/common/websocket/WsDataTest.java
@@ -0,0 +1,17 @@
+package io.openems.common.websocket;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class WsDataTest {
+
+ @Test
+ public void test() {
+ var sut = new WsData(null);
+ assertEquals("", sut.toLogString());
+
+ sut.dispose();
+ }
+
+}
diff --git a/io.openems.edge.application/.classpath b/io.openems.edge.application/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.application/.classpath
+++ b/io.openems.edge.application/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.application/EdgeApp.bndrun b/io.openems.edge.application/EdgeApp.bndrun
index 11442b68bce..c1afa91dad3 100644
--- a/io.openems.edge.application/EdgeApp.bndrun
+++ b/io.openems.edge.application/EdgeApp.bndrun
@@ -1,5 +1,5 @@
-runfw: org.apache.felix.framework;version='[7.0.5,7.0.5]'
--runee: JavaSE-17
+-runee: JavaSE-21
-runprovidedcapabilities: ${native_capability}
-resolve.effective: active
@@ -31,6 +31,7 @@
bnd.identity;id='org.ops4j.pax.logging.pax-logging-api',\
bnd.identity;id='org.ops4j.pax.logging.pax-logging-log4j2',\
bnd.identity;id='org.apache.felix.http.jetty',\
+ bnd.identity;id='org.apache.felix.http.servlet-api',\
bnd.identity;id='org.apache.felix.webconsole',\
bnd.identity;id='org.apache.felix.webconsole.plugins.ds',\
bnd.identity;id='org.apache.felix.inventory',\
@@ -74,6 +75,7 @@
bnd.identity;id='io.openems.edge.controller.ess.delaycharge',\
bnd.identity;id='io.openems.edge.controller.ess.delayedselltogrid',\
bnd.identity;id='io.openems.edge.controller.ess.emergencycapacityreserve',\
+ bnd.identity;id='io.openems.edge.controller.ess.fastfrequencyreserve',\
bnd.identity;id='io.openems.edge.controller.ess.fixactivepower',\
bnd.identity;id='io.openems.edge.controller.ess.fixstateofcharge',\
bnd.identity;id='io.openems.edge.controller.ess.gridoptimizedcharge',\
@@ -122,6 +124,7 @@
bnd.identity;id='io.openems.edge.evcs.goe.chargerhome',\
bnd.identity;id='io.openems.edge.evcs.hardybarth',\
bnd.identity;id='io.openems.edge.evcs.keba.kecontact',\
+ bnd.identity;id='io.openems.edge.evcs.mennekes',\
bnd.identity;id='io.openems.edge.evcs.ocpp.abl',\
bnd.identity;id='io.openems.edge.evcs.ocpp.common',\
bnd.identity;id='io.openems.edge.evcs.ocpp.ies.keywatt.singleccs',\
@@ -139,6 +142,7 @@
bnd.identity;id='io.openems.edge.io.offgridswitch',\
bnd.identity;id='io.openems.edge.io.revpi',\
bnd.identity;id='io.openems.edge.io.shelly',\
+ bnd.identity;id='io.openems.edge.io.siemenslogo',\
bnd.identity;id='io.openems.edge.io.wago',\
bnd.identity;id='io.openems.edge.io.weidmueller',\
bnd.identity;id='io.openems.edge.kaco.blueplanet.hybrid10',\
@@ -166,6 +170,7 @@
bnd.identity;id='io.openems.edge.meter.weidmueller',\
bnd.identity;id='io.openems.edge.meter.ziehl',\
bnd.identity;id='io.openems.edge.onewire.thermometer',\
+ bnd.identity;id='io.openems.edge.predictor.lstm',\
bnd.identity;id='io.openems.edge.predictor.persistencemodel',\
bnd.identity;id='io.openems.edge.predictor.similardaymodel',\
bnd.identity;id='io.openems.edge.pvinverter.cluster',\
@@ -189,21 +194,22 @@
bnd.identity;id='io.openems.edge.timeofusetariff.groupe',\
bnd.identity;id='io.openems.edge.timeofusetariff.hassfurt',\
bnd.identity;id='io.openems.edge.timeofusetariff.rabotcharge',\
+ bnd.identity;id='io.openems.edge.timeofusetariff.swisspower',\
bnd.identity;id='io.openems.edge.timeofusetariff.tibber',\
-
+
-runbundles: \
Java-WebSocket;version='[1.5.4,1.5.5)',\
- bcpkix;version='[1.70.0,1.70.1)',\
- bcprov;version='[1.70.0,1.70.1)',\
- bcutil;version='[1.70.0,1.70.1)',\
+ bcpkix;version='[1.79.0,1.79.1)',\
+ bcprov;version='[1.79.0,1.79.1)',\
+ bcutil;version='[1.79.0,1.79.1)',\
com.fasterxml.aalto-xml;version='[1.3.3,1.3.4)',\
com.fazecast.jSerialComm;version='[2.10.4,2.10.5)',\
com.ghgande.j2mod;version='[3.2.1,3.2.2)',\
com.google.gson;version='[2.11.0,2.11.1)',\
- com.google.guava;version='[33.3.1,33.3.2)',\
+ com.google.guava;version='[33.4.0,33.4.1)',\
com.google.guava.failureaccess;version='[1.0.2,1.0.3)',\
com.squareup.okio;version='[3.9.1,3.9.2)',\
- com.sun.jna;version='[5.15.0,5.15.1)',\
+ com.sun.jna;version='[5.16.0,5.16.1)',\
io.openems.common;version=snapshot,\
io.openems.edge.application;version=snapshot,\
io.openems.edge.battery.api;version=snapshot,\
@@ -245,6 +251,7 @@
io.openems.edge.controller.ess.delaycharge;version=snapshot,\
io.openems.edge.controller.ess.delayedselltogrid;version=snapshot,\
io.openems.edge.controller.ess.emergencycapacityreserve;version=snapshot,\
+ io.openems.edge.controller.ess.fastfrequencyreserve;version=snapshot,\
io.openems.edge.controller.ess.fixactivepower;version=snapshot,\
io.openems.edge.controller.ess.fixstateofcharge;version=snapshot,\
io.openems.edge.controller.ess.gridoptimizedcharge;version=snapshot,\
@@ -296,6 +303,7 @@
io.openems.edge.evcs.goe.chargerhome;version=snapshot,\
io.openems.edge.evcs.hardybarth;version=snapshot,\
io.openems.edge.evcs.keba.kecontact;version=snapshot,\
+ io.openems.edge.evcs.mennekes;version=snapshot,\
io.openems.edge.evcs.ocpp.abl;version=snapshot,\
io.openems.edge.evcs.ocpp.common;version=snapshot,\
io.openems.edge.evcs.ocpp.ies.keywatt.singleccs;version=snapshot,\
@@ -314,6 +322,7 @@
io.openems.edge.io.offgridswitch;version=snapshot,\
io.openems.edge.io.revpi;version=snapshot,\
io.openems.edge.io.shelly;version=snapshot,\
+ io.openems.edge.io.siemenslogo;version=snapshot,\
io.openems.edge.io.wago;version=snapshot,\
io.openems.edge.io.weidmueller;version=snapshot,\
io.openems.edge.kaco.blueplanet.hybrid10;version=snapshot,\
@@ -343,6 +352,7 @@
io.openems.edge.meter.ziehl;version=snapshot,\
io.openems.edge.onewire.thermometer;version=snapshot,\
io.openems.edge.predictor.api;version=snapshot,\
+ io.openems.edge.predictor.lstm;version=snapshot,\
io.openems.edge.predictor.persistencemodel;version=snapshot,\
io.openems.edge.predictor.similardaymodel;version=snapshot,\
io.openems.edge.pvinverter.api;version=snapshot,\
@@ -371,6 +381,7 @@
io.openems.edge.timeofusetariff.groupe;version=snapshot,\
io.openems.edge.timeofusetariff.hassfurt;version=snapshot,\
io.openems.edge.timeofusetariff.rabotcharge;version=snapshot,\
+ io.openems.edge.timeofusetariff.swisspower;version=snapshot,\
io.openems.edge.timeofusetariff.tibber;version=snapshot,\
io.openems.oem.openems;version=snapshot,\
io.openems.shared.influxdb;version=snapshot,\
@@ -393,9 +404,8 @@
io.openems.wrapper.retrofit-converter-scalars;version=snapshot,\
io.openems.wrapper.retrofit2;version=snapshot,\
io.openems.wrapper.sdnotify;version=snapshot,\
- io.reactivex.rxjava3.rxjava;version='[3.1.9,3.1.10)',\
+ io.reactivex.rxjava3.rxjava;version='[3.1.10,3.1.11)',\
javax.jmdns;version='[3.4.1,3.4.2)',\
- javax.xml.soap-api;version='[1.4.0,1.4.1)',\
org.apache.commons.commons-codec;version='[1.17.1,1.17.2)',\
org.apache.commons.commons-compress;version='[1.27.1,1.27.2)',\
org.apache.commons.commons-csv;version='[1.11.0,1.11.1)',\
@@ -416,13 +426,13 @@
org.eclipse.jetty.io;version='[9.4.28,9.4.29)',\
org.eclipse.jetty.util;version='[9.4.28,9.4.29)',\
org.eclipse.paho.mqttv5.client;version='[1.2.5,1.2.6)',\
- org.jetbrains.kotlin.osgi-bundle;version='[2.0.20,2.0.21)',\
- org.jsoup;version='[1.18.1,1.18.2)',\
+ org.jetbrains.kotlin.osgi-bundle;version='[2.1.0,2.1.1)',\
+ org.jsoup;version='[1.18.3,1.18.4)',\
org.jsr-305;version='[3.0.2,3.0.3)',\
org.openmuc.jmbus;version='[3.3.0,3.3.1)',\
org.openmuc.jrxtx;version='[1.0.1,1.0.2)',\
- org.ops4j.pax.logging.pax-logging-api;version='[2.2.1,2.2.2)',\
- org.ops4j.pax.logging.pax-logging-log4j2;version='[2.2.1,2.2.2)',\
+ org.ops4j.pax.logging.pax-logging-api;version='[2.2.7,2.2.8)',\
+ org.ops4j.pax.logging.pax-logging-log4j2;version='[2.2.7,2.2.8)',\
org.osgi.service.component;version='[1.5.1,1.5.2)',\
org.osgi.util.function;version='[1.2.0,1.2.1)',\
org.osgi.util.promise;version='[1.3.0,1.3.1)',\
diff --git a/io.openems.edge.battery.api/.classpath b/io.openems.edge.battery.api/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.battery.api/.classpath
+++ b/io.openems.edge.battery.api/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.battery.api/test/io/openems/edge/battery/protection/BatteryProtectionTest.java b/io.openems.edge.battery.api/test/io/openems/edge/battery/protection/BatteryProtectionTest.java
index de029ad3ba5..74b3d0f4895 100644
--- a/io.openems.edge.battery.api/test/io/openems/edge/battery/protection/BatteryProtectionTest.java
+++ b/io.openems.edge.battery.api/test/io/openems/edge/battery/protection/BatteryProtectionTest.java
@@ -1,14 +1,24 @@
package io.openems.edge.battery.protection;
+import static io.openems.edge.battery.api.Battery.ChannelId.CHARGE_MAX_CURRENT;
+import static io.openems.edge.battery.api.Battery.ChannelId.DISCHARGE_MAX_CURRENT;
+import static io.openems.edge.battery.api.Battery.ChannelId.MAX_CELL_TEMPERATURE;
+import static io.openems.edge.battery.api.Battery.ChannelId.MAX_CELL_VOLTAGE;
+import static io.openems.edge.battery.api.Battery.ChannelId.MIN_CELL_TEMPERATURE;
+import static io.openems.edge.battery.api.Battery.ChannelId.MIN_CELL_VOLTAGE;
+import static io.openems.edge.battery.protection.BatteryProtection.ChannelId.BP_CHARGE_BMS;
+import static io.openems.edge.battery.protection.BatteryProtection.ChannelId.BP_DISCHARGE_BMS;
+import static io.openems.edge.common.startstop.StartStoppable.ChannelId.START_STOP;
+import static java.time.temporal.ChronoUnit.MINUTES;
+import static java.time.temporal.ChronoUnit.SECONDS;
+
import java.time.Instant;
import java.time.ZoneOffset;
-import java.time.temporal.ChronoUnit;
import org.junit.Test;
import io.openems.common.channel.Unit;
import io.openems.common.test.TimeLeapClock;
-import io.openems.common.types.ChannelAddress;
import io.openems.common.types.OpenemsType;
import io.openems.edge.battery.protection.currenthandler.ChargeMaxCurrentHandler;
import io.openems.edge.battery.protection.currenthandler.DischargeMaxCurrentHandler;
@@ -18,7 +28,6 @@
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.linecharacteristic.PolyLine;
import io.openems.edge.common.startstop.StartStop;
-import io.openems.edge.common.startstop.StartStoppable;
import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyComponentManager;
@@ -102,22 +111,6 @@ public Doc doc() {
private static final String BATTERY_ID = "battery0";
- private static final ChannelAddress BATTERY_START_STOP = new ChannelAddress(BATTERY_ID,
- StartStoppable.ChannelId.START_STOP.id());
- private static final ChannelAddress BATTERY_BP_CHARGE_BMS = new ChannelAddress(BATTERY_ID,
- BatteryProtection.ChannelId.BP_CHARGE_BMS.id());
- private static final ChannelAddress BATTERY_BP_DISCHARGE_BMS = new ChannelAddress(BATTERY_ID,
- BatteryProtection.ChannelId.BP_DISCHARGE_BMS.id());
- private static final ChannelAddress BATTERY_MIN_CELL_VOLTAGE = new ChannelAddress(BATTERY_ID, "MinCellVoltage");
- private static final ChannelAddress BATTERY_MAX_CELL_VOLTAGE = new ChannelAddress(BATTERY_ID, "MaxCellVoltage");
- private static final ChannelAddress BATTERY_MIN_CELL_TEMPERATURE = new ChannelAddress(BATTERY_ID,
- "MinCellTemperature");
- private static final ChannelAddress BATTERY_MAX_CELL_TEMPERATURE = new ChannelAddress(BATTERY_ID,
- "MaxCellTemperature");
- private static final ChannelAddress BATTERY_CHARGE_MAX_CURRENT = new ChannelAddress(BATTERY_ID, "ChargeMaxCurrent");
- private static final ChannelAddress BATTERY_DISCHARGE_MAX_CURRENT = new ChannelAddress(BATTERY_ID,
- "DischargeMaxCurrent");
-
@Test
public void test() throws Exception {
final var battery = new DummyBattery(BATTERY_ID);
@@ -137,169 +130,168 @@ public void test() throws Exception {
.setForceCharge(FORCE_CHARGE) //
.build()) //
.build();
- new ComponentTest(new DummyBattery(BATTERY_ID)) //
- .addComponent(battery) //
+ new ComponentTest(battery) //
.next(new TestCase() //
- .input(BATTERY_START_STOP, StartStop.START) //
- .input(BATTERY_BP_CHARGE_BMS, 80) //
- .input(BATTERY_BP_DISCHARGE_BMS, 80) //
- .input(BATTERY_MIN_CELL_VOLTAGE, 2950) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3300) //
- .input(BATTERY_MIN_CELL_TEMPERATURE, 16) //
- .input(BATTERY_MAX_CELL_TEMPERATURE, 17) //
+ .input(START_STOP, StartStop.START) //
+ .input(BP_CHARGE_BMS, 80) //
+ .input(BP_DISCHARGE_BMS, 80) //
+ .input(MIN_CELL_VOLTAGE, 2950) //
+ .input(MAX_CELL_VOLTAGE, 3300) //
+ .input(MIN_CELL_TEMPERATURE, 16) //
+ .input(MAX_CELL_TEMPERATURE, 17) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 0)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 0)) //
.next(new TestCase("open, but maxIncreaseAmpereLimit") //
- .timeleap(clock, 2, ChronoUnit.SECONDS) //
- .input(BATTERY_MIN_CELL_VOLTAGE, 3000) //
+ .timeleap(clock, 2, SECONDS) //
+ .input(MIN_CELL_VOLTAGE, 3000) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 1) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 1)) //
+ .output(CHARGE_MAX_CURRENT, 1) //
+ .output(DISCHARGE_MAX_CURRENT, 1)) //
.next(new TestCase() //
- .timeleap(clock, 2, ChronoUnit.SECONDS) //
- .input(BATTERY_MIN_CELL_VOLTAGE, 3050) //
+ .timeleap(clock, 2, SECONDS) //
+ .input(MIN_CELL_VOLTAGE, 3050) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 2) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 2)) //
+ .output(CHARGE_MAX_CURRENT, 2) //
+ .output(DISCHARGE_MAX_CURRENT, 2)) //
.next(new TestCase() //
- .timeleap(clock, 10, ChronoUnit.SECONDS) //
+ .timeleap(clock, 10, SECONDS) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 7) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 7)) //
+ .output(CHARGE_MAX_CURRENT, 7) //
+ .output(DISCHARGE_MAX_CURRENT, 7)) //
.next(new TestCase() //
- .timeleap(clock, 10, ChronoUnit.MINUTES) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3300) //
+ .timeleap(clock, 10, MINUTES) //
+ .input(MAX_CELL_VOLTAGE, 3300) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 80) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 80) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase() //
- .timeleap(clock, 10, ChronoUnit.MINUTES) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3499) //
+ .timeleap(clock, 10, MINUTES) //
+ .input(MAX_CELL_VOLTAGE, 3499) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 54) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 54) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase() //
- .timeleap(clock, 10, ChronoUnit.MINUTES) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3649) //
+ .timeleap(clock, 10, MINUTES) //
+ .input(MAX_CELL_VOLTAGE, 3649) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 2) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 2) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase() //
- .timeleap(clock, 10, ChronoUnit.MINUTES) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3649) //
+ .timeleap(clock, 10, MINUTES) //
+ .input(MAX_CELL_VOLTAGE, 3649) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 2) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 2) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase() //
- .timeleap(clock, 10, ChronoUnit.MINUTES) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3650) //
+ .timeleap(clock, 10, MINUTES) //
+ .input(MAX_CELL_VOLTAGE, 3650) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Start Force-Discharge: wait 60 seconds") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3660) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3660) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Start Force-Discharge") //
- .timeleap(clock, 60, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3660) //
+ .timeleap(clock, 60, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3660) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -2) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -2) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Force-Discharge") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3640) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3640) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -2) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -2) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #1") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3639) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3639) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -1) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -1) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #1 still reduce by 1") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3638) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3638) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -1) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -1) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #1 still reduce by 1") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3610) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3610) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #2") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3600) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3600) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Start Force-Discharge again") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3660) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3660) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -2) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -2) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Force-Discharge") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3640) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3640) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -2) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -2) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #1") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3639) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3639) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -1) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -1) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #1 still reduce by 1") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3638) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3638) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, -1) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, -1) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #1 still reduce by 1") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3637) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3637) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #2") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3600) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3600) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Block Charge #3") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3450) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3450) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Finish Force-Discharge") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3449) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3449) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase() //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3400) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3400) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 0) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 0) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
.next(new TestCase("Allow Charge") //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(BATTERY_MAX_CELL_VOLTAGE, 3350) //
+ .timeleap(clock, 1, SECONDS) //
+ .input(MAX_CELL_VOLTAGE, 3350) //
.onAfterProcessImage(() -> sut.apply()) //
- .output(BATTERY_CHARGE_MAX_CURRENT, 1) //
- .output(BATTERY_DISCHARGE_MAX_CURRENT, 80)) //
+ .output(CHARGE_MAX_CURRENT, 1) //
+ .output(DISCHARGE_MAX_CURRENT, 80)) //
;
}
diff --git a/io.openems.edge.battery.bmw/.classpath b/io.openems.edge.battery.bmw/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.battery.bmw/.classpath
+++ b/io.openems.edge.battery.bmw/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.battery.bmw/bnd.bnd b/io.openems.edge.battery.bmw/bnd.bnd
index 8f2cc726fe3..9fbb783f822 100644
--- a/io.openems.edge.battery.bmw/bnd.bnd
+++ b/io.openems.edge.battery.bmw/bnd.bnd
@@ -8,8 +8,10 @@ Bundle-Version: 1.0.0.${tstamp}
com.ghgande.j2mod,\
io.openems.common,\
io.openems.edge.battery.api,\
+ io.openems.edge.bridge.http,\
io.openems.edge.bridge.modbus,\
- io.openems.edge.common
+ io.openems.edge.common,\
+ io.openems.edge.io.api,\
-testpath: \
${testpath}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryBmw.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryBmw.java
new file mode 100644
index 00000000000..c6d0dcd6fcf
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryBmw.java
@@ -0,0 +1,595 @@
+package io.openems.edge.battery.bmw;
+
+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.types.OpenemsType;
+import io.openems.edge.battery.api.Battery;
+import io.openems.edge.battery.bmw.enums.BatteryState;
+import io.openems.edge.battery.bmw.enums.BatteryStateCommand;
+import io.openems.edge.battery.bmw.statemachine.GoRunningSubState;
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.bridge.modbus.api.ModbusComponent;
+import io.openems.edge.common.channel.Channel;
+import io.openems.edge.common.channel.Doc;
+import io.openems.edge.common.channel.StateChannel;
+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;
+import io.openems.edge.common.startstop.StartStoppable;
+
+public interface BatteryBmw extends Battery, ModbusComponent, OpenemsComponent, StartStoppable {
+
+ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId {
+ STATE_MACHINE(Doc.of(State.values()) //
+ .text("Current state of State-Machine")), //
+ RUN_FAILED(Doc.of(Level.FAULT) //
+ .text("Running the Logic failed")), //
+ ERROR_BATTERY_TYPE(Doc.of(Level.FAULT) //
+ .text("Configuring the Battery Type not successful!")), //
+ UNEXPECTED_STOPPED_STATE(Doc.of(Level.FAULT) //
+ .text("Unexpected battery state in \"Stopped\"!")),
+ UNEXPECTED_RUNNING_STATE(Doc.of(Level.FAULT) //
+ .text("Unexpected battery state in \"Running\"!")),
+ SOC_RAW_VALUE(Doc.of(OpenemsType.INTEGER)//
+ .unit(Unit.THOUSANDTH)//
+ .accessMode(AccessMode.READ_ONLY)), //
+ TIMEOUT_START_BATTERY(Doc.of(Level.FAULT) //
+ .text("The maximum start time is passed")), //
+ TIMEOUT_STOP_BATTERY(Doc.of(Level.FAULT) //
+ .text("The maximum stop time is passed")), //
+ GO_RUNNING_STATE_MACHINE(Doc.of(GoRunningSubState.values()) //
+ .text("Current State of GoRunning State-Machine")), //
+ /*
+ * ErrBits1
+ */
+
+ UNSPECIFIED_ERROR(Doc.of(Level.FAULT) //
+ .text("Unspecified Error - Cell-config-Error, Slave-count-Error")), //
+
+ LOW_VOLTAGE_ERROR(Doc.of(Level.FAULT) //
+ .text("Low Voltage Error - Cell voltage minimal")), //
+
+ HIGH_VOLTAGE_ERROR(Doc.of(Level.FAULT) //
+ .text("High Voltage Error - Cell voltage maximal")), //
+
+ CHARGE_CURRENT_ERROR(Doc.of(Level.FAULT) //
+ .text("Charge Current Error - Imax-HW, Imax-SW, I-High (e.g. current dependend on temperature")), //
+
+ DISCHARGE_CURRENT_ERROR(Doc.of(Level.FAULT) //
+ .text("Discharge Current Error - Imax-HW, Imax-SW, I-High (e.g. current dependend on temperature")), //
+
+ CHARGE_POWER_ERROR(Doc.of(Level.FAULT) //
+ .text("Charge Power Error")), //
+
+ DISCHARGE_POWER_ERROR(Doc.of(Level.FAULT) //
+ .text("Discharge Power Error")), //
+
+ LOW_SOC_ERROR(Doc.of(Level.FAULT) //
+ .text("Low SOC Error")), //
+
+ HIGH_SOC_ERROR(Doc.of(Level.FAULT) //
+ .text("High SOC Error")), //
+
+ LOW_TEMPERATURE_ERROR(Doc.of(Level.FAULT) //
+ .text("Low Temperature Error - Cell temperature minimal")), //
+
+ HIGH_TEMPERATURE_ERROR(Doc.of(Level.FAULT) //
+ .text("High Temperature Error - Cell temperature maximal")), //
+
+ INSULATION_ERROR(Doc.of(Level.FAULT) //
+ .text("Insulation Error - I-Diff error (self test error, I-Diff > |300 mA|)")), //
+
+ CONTACTOR_ERROR(Doc.of(Level.FAULT) //
+ .text("Contactor Error (contactor feedback signals")), //
+
+ SENSOR_ERROR(Doc.of(Level.FAULT) //
+ .text("Sensor Error - Current sensor error")), //
+
+ IMBALANCE_ERROR(Doc.of(Level.FAULT) //
+ .text("Imbalance Error - Static and dynamic cell imbalance (voltage)")), //
+
+ COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ .text("Communication Error - Batcom Error (Timeout), Master-Slave Can Error (Timeout)")), //
+
+ /*
+ * ErrBits2
+ */
+
+ CONTAINER_ERROR(Doc.of(Level.FAULT) //
+ .text("Container/(Room) Error")), //
+
+ SOH_ERROR(Doc.of(Level.FAULT) //
+ .text("SOH Error")), //
+
+ RACK_STING_ERROR(Doc.of(Level.FAULT) //
+ .text("Rack/String Error")), //
+
+ /*
+ * WarnBits1
+ */
+
+ UNSPECIFIED_WARNING(Doc.of(Level.WARNING) //
+ .text("Unspecified Warning - Cell-config-Error, Slave-count-Error")), //
+
+ LOW_VOLTAGE_WARNING(Doc.of(Level.WARNING) //
+ .text("Low Voltage Error - Cell voltage high")), //
+
+ HIGH_VOLTAGE_WARNING(Doc.of(Level.WARNING) //
+ .text("High Voltage Warning - Cell voltage high")), //
+
+ CHARGE_CURRENT_WARNING(Doc.of(Level.WARNING) //
+ .text("Charge Current Warning - Imax-HW, Imax-SW, I-High (e.g. current dependend on temperature")), //
+
+ DISCHARGE_CURRENT_WARNING(Doc.of(Level.WARNING) //
+ .text("Discharge Current Warning - Imax-HW, Imax-SW, I-High (e.g. current dependend on temperature")), //
+
+ CHARGE_POWER_WARNING(Doc.of(Level.WARNING) //
+ .text("Charge Power Warning")), //
+
+ DISCHARGE_POWER_WARNING(Doc.of(Level.WARNING) //
+ .text("Discharge Power Warning")), //
+
+ LOW_SOC_WARNING(Doc.of(Level.WARNING) //
+ .text("Low SOC Warning")), //
+
+ HIGH_SOC_WARNING(Doc.of(Level.WARNING) //
+ .text("High SOC Warning")), //
+
+ LOW_TEMPERATURE_WARNING(Doc.of(Level.WARNING) //
+ .text("Low Temperature Warning - Cell temperature high")), //
+
+ HIGH_TEMPERATURE_WARNING(Doc.of(Level.WARNING) //
+ .text("High Temperature Warning - Cell temperature high")), //
+
+ INSULATION_WARNING(Doc.of(Level.WARNING) //
+ .text("Insulation Warning - I-Diff error (self test error, I-Diff > |300 mA|)")), //
+
+ CONTACTOR_WARNING(Doc.of(Level.WARNING) //
+ .text("Contactor Warning (contactor feedback signals")), //
+
+ SENSOR_WARNING(Doc.of(Level.WARNING) //
+ .text("Sensor Warning - Current sensor error")), //
+
+ IMBALANCE_WARNING(Doc.of(Level.WARNING) //
+ .text("Imbalance Warning - Static and dynamic cell imbalance (voltage)")), //
+
+ COMMUNICATION_WARNING(Doc.of(Level.WARNING) //
+ .text("Communication Warning - Batcom Error (Timeout), Master-Slave Can Error (Timeout)")), //
+
+ // WarnBits2
+ CONTAINER_WARNING(Doc.of(Level.WARNING) //
+ .text("Container/(Room) Warning")), //
+
+ SOH_WARNING(Doc.of(Level.WARNING) //
+ .text("SOH Warning")), //
+
+ RACK_STING_WARNING(Doc.of(Level.WARNING) //
+ .text("Rack/String Warning - min. 1 string is in error condition (disconnected)")), //
+
+ // Read / write channels
+ BATTERY_STATE_COMMAND(Doc.of(BatteryStateCommand.values()) //
+ .accessMode(AccessMode.READ_WRITE)), //
+
+ BATTERY_STATE(Doc.of(BatteryState.values()) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .persistencePriority(PersistencePriority.HIGH)), //
+
+ MAX_OPERATING_CURRENT(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.AMPERE) //
+ .text("Absolute maximum operating (max. discharge current) current of battery")), //
+
+ MIN_OPERATING_CURRENT(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.AMPERE) //
+ .text("Absolute minimum operating current (max. charge current) of battery")), //
+
+ MAX_DYNAMIC_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.DEZIVOLT)), //
+
+ MIN_DYNAMIC_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.DEZIVOLT)), //
+
+ CONNECTED_STRING_NUMBER(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY)), //
+
+ INSTALLED_STRING_NUMBER(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY)), //
+
+ BATTERY_TOTAL_SOC(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.TENTHOUSANDTH) //
+ .text("Battery state of charge calculated for all strings, which are available (connected and not connected)")), //
+
+ BATTERY_SOC(Doc.of(OpenemsType.DOUBLE) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.TENTHOUSANDTH) //
+ .onChannelChange(BatteryBmwImpl::updateSoc)), //
+
+ REMAINING_CHARGE_CAPACITY(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.AMPERE_HOURS)), //
+
+ REMAINING_DISCHARGE_CAPACITY(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.AMPERE_HOURS) //
+ .text("Remaining discharge capacity - Ah possible to charge from now on")), //
+
+ REMANING_CHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.KILOWATT_HOURS) //
+ .text("Remaining energy to charge")), //
+
+ REMANING_DISCHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.KILOWATT_HOURS) //
+ .text("Remaining energy to discharge")), //
+
+ NOMINAL_ENERGY(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.KILOWATT_HOURS) //
+ .text("Battery Nominal Energy (connected Racks)")), //
+
+ NOMINAL_ENERGY_TOTAL(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.KILOWATT_HOURS) //
+ .text("Battery Nominal Energy (all Racks)")), //
+
+ NOMINAL_CAPACITY(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.AMPERE_HOURS) //
+ .text("Battery Nominal Capacity (connected Racks)")), //
+
+ // External voltage (at DC connector) of the battery
+ LINK_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.DEZIVOLT) //
+ .onChannelChange(BatteryBmwImpl::updateVoltage)), //
+
+ INTERNAL_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.DEZIVOLT) //
+ .onChannelChange(BatteryBmwImpl::updateVoltage)), //
+
+ AVG_BATTERY_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
+ .unit(Unit.DEZIDEGREE_CELSIUS)), //
+
+ MIN_BATTERY_TEMPERATURE(Doc.of(OpenemsType.INTEGER)//
+ .unit(Unit.DEZIDEGREE_CELSIUS)), //
+
+ MAX_BATTERY_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
+ .unit(Unit.DEZIDEGREE_CELSIUS)), //
+
+ INSULATION_RESISTANCE(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.KILOOHM) //
+ .text("Insulation Resistanc")), //
+
+ DISCHARGE_MAX_CURRENT_HIGH_RESOLUTION(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.DEZIAMPERE) //
+ .text("Battery maximum limit dynamic current (max. discharge current)")), //
+
+ CHARGE_MAX_CURRENT_HIGH_RESOLUTION(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .unit(Unit.DEZIAMPERE) //
+ .text("Battery minimum limit dynamic current (max. charge current)")), //
+
+ FULL_CYCLE_COUNT(Doc.of(OpenemsType.INTEGER) //
+ .accessMode(AccessMode.READ_ONLY) //
+ .text("Battery Full Cycles Count - count complete energy throughputs")), //
+
+ // actual not implemented @ BCS side
+ HEART_BEAT(Doc.of(OpenemsType.INTEGER)//
+ .accessMode(AccessMode.READ_ONLY) //
+ .text("Life sign signal")), //
+
+ AVG_CELL_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
+ .unit(Unit.DEGREE_CELSIUS)), //
+ ; //
+
+ private final Doc doc;
+
+ private ChannelId(Doc doc) {
+ this.doc = doc;
+ }
+
+ @Override
+ public Doc doc() {
+ return this.doc;
+ }
+
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#STATE_MACHINE}.
+ *
+ * @return the Channel
+ */
+ public default Channel getStateMachineChannel() {
+ return this.channel(ChannelId.STATE_MACHINE);
+ }
+
+ /**
+ * Gets the {@link StateChannel} for {@link ChannelId#STATE_MACHINE}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getStateMachine() {
+ return this.getStateMachineChannel().value();
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on {@link ChannelId#STATE_MACHINE}
+ * Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setStateMachine(State value) {
+ this.getStateMachineChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#RUN_FAILED}.
+ *
+ * @return the Channel
+ */
+ public default Channel getRunFailedChannel() {
+ return this.channel(ChannelId.RUN_FAILED);
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on {@link ChannelId#RUN_FAILED}
+ * Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setRunFailed(boolean value) {
+ this.getRunFailedChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#UNEXPECTED_STOPPED_STATE}.
+ *
+ * @return the Channel
+ */
+ public default StateChannel getUnexpectedStoppedStateChannel() {
+ return this.channel(ChannelId.UNEXPECTED_STOPPED_STATE);
+ }
+
+ /**
+ * Gets the {@link StateChannel} for {@link ChannelId#UNEXPECTED_STOPPED_STATE}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getUnexpectedStoppedState() {
+ return this.getUnexpectedStoppedStateChannel().value();
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on
+ * {@link ChannelId#UNEXPECTED_STOPPED_STATE} Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setUnexpectedStoppedState(boolean value) {
+ this.getUnexpectedStoppedStateChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#UNEXPECTED_RUNNING_STATE}.
+ *
+ * @return the Channel
+ */
+ public default StateChannel getUnexpectedRunningStateChannel() {
+ return this.channel(ChannelId.UNEXPECTED_RUNNING_STATE);
+ }
+
+ /**
+ * Gets the {@link StateChannel} for {@link ChannelId#UNEXPECTED_RUNNING_STATE}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getUnexpectedRunningState() {
+ return this.getUnexpectedRunningStateChannel().value();
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on
+ * {@link ChannelId#UNEXPECTED_RUNNING_STATE} Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setUnexpectedRunningState(boolean value) {
+ this.getUnexpectedRunningStateChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#TIMEOUT_START_BATTERY}.
+ *
+ * @return the Channel
+ */
+ public default StateChannel getTimeoutStartBatteryChannel() {
+ return this.channel(ChannelId.TIMEOUT_START_BATTERY);
+ }
+
+ /**
+ * Gets the {@link StateChannel} for {@link ChannelId#TIMEOUT_START_BATTERY}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getTimeoutStartBattery() {
+ return this.getTimeoutStartBatteryChannel().value();
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on
+ * {@link ChannelId#TIMEOUT_START_BATTERY} Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setTimeoutStartBattery(Boolean value) {
+ this.getTimeoutStartBatteryChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#TIMEOUT_STOP_BATTERY}.
+ *
+ * @return the Channel
+ */
+ public default StateChannel getTimeoutStopBatteryChannel() {
+ return this.channel(ChannelId.TIMEOUT_STOP_BATTERY);
+ }
+
+ /**
+ * Gets the {@link StateChannel} for {@link ChannelId#TIMEOUT_STOP_BATTERY}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getTimeoutStopBattery() {
+ return this.getTimeoutStopBatteryChannel().value();
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on
+ * {@link ChannelId#TIMEOUT_STOP_BATTERY} Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setTimeoutStopBattery(Boolean value) {
+ this.getTimeoutStopBatteryChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#BATTERY_STATE_COMMAND}.
+ *
+ * @return the Channel {@link Channel}
+ */
+ public default WriteChannel getBatteryStateCommandChannel() {
+ return this.channel(ChannelId.BATTERY_STATE_COMMAND);
+ }
+
+ /**
+ * See {@link ChannelId#BATTERY_STATE_COMMAND}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getBatteryStateCommand() {
+ return this.getBatteryStateCommandChannel().value();
+ }
+
+ /**
+ * See {@link ChannelId#BATTERY_STATE_COMMAND}.
+ *
+ * @param value the next write value
+ * @throws OpenemsNamedException on error
+ */
+ public default void setBatteryStateCommand(BatteryStateCommand value) throws OpenemsNamedException {
+ this.getBatteryStateCommandChannel().setNextWriteValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#BATTERY_STATE}.
+ *
+ * @return the Channel {@link Channel}
+ */
+ public default Channel getBatteryStateChannel() {
+ return this.channel(ChannelId.BATTERY_STATE);
+ }
+
+ /**
+ * See {@link ChannelId#BATTERY_STATE}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getBatteryState() {
+ return this.getBatteryStateChannel().value();
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#LINK_VOLTAGE}.
+ *
+ * @return the Channel
+ */
+ public default Channel getLinkVoltageChannel() {
+ return this.channel(ChannelId.LINK_VOLTAGE);
+ }
+
+ /**
+ * Gets the LinkVoltage, see {@link ChannelId#LINK_VOLTAGE}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getLinkVoltage() {
+ return this.getLinkVoltageChannel().value();
+ }
+
+ /*
+ * Gets the Channel for {@link ChannelId#INTERNAL_VOLTAGE}.
+ *
+ * @return the Channel
+ */
+ public default Channel getInternalVoltageChannel() {
+ return this.channel(ChannelId.INTERNAL_VOLTAGE);
+ }
+
+ /**
+ * Gets the {@link StateChannel} for {@link ChannelId#INTERNAL_VOLTAGE}.
+ *
+ * @return the Channel {@link Value}
+ */
+ public default Value getInternalVoltage() {
+ return this.getInternalVoltageChannel().value();
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#GO_RUNNING_STATE_MACHINE}.
+ *
+ * @return the Channel
+ */
+ public default Channel getGoRunningStateMachineChannel() {
+ return this.channel(ChannelId.GO_RUNNING_STATE_MACHINE);
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on
+ * {@link ChannelId#GO_RUNNING_STATE_MACHINE} Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setGoRunningStateMachine(GoRunningSubState value) {
+ this.getGoRunningStateMachineChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the Channel for {@link ChannelId#SOC_RAW_VALUE}.
+ *
+ * @return the Channel
+ */
+ public default Channel getSocRawValueChannel() {
+ return this.channel(ChannelId.SOC_RAW_VALUE);
+ }
+
+ /**
+ * Internal method to set the 'nextValue' on {@link ChannelId#SOC_RAW_VALUE}
+ * Channel.
+ *
+ * @param value the next value
+ */
+ public default void _setSocRawValue(int value) {
+ this.getSocRawValueChannel().setNextValue(value);
+ }
+
+ /**
+ * Gets the target Start/Stop mode from config or StartStop-Channel.
+ *
+ * @return {@link StartStop}
+ */
+ public StartStop getStartStopTarget();
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryBmwImpl.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryBmwImpl.java
new file mode 100644
index 00000000000..959dfcfcf01
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryBmwImpl.java
@@ -0,0 +1,420 @@
+package io.openems.edge.battery.bmw;
+
+import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.DIRECT_1_TO_1;
+import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.INVERT;
+import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
+import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_2;
+
+import java.util.Map;
+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;
+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.EventHandler;
+import org.osgi.service.event.propertytypes.EventTopics;
+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.common.oem.OpenemsEdgeOem;
+import io.openems.edge.battery.api.Battery;
+import io.openems.edge.battery.bmw.enums.BatteryState;
+import io.openems.edge.battery.bmw.enums.BatteryStateCommand;
+import io.openems.edge.battery.bmw.statemachine.Context;
+import io.openems.edge.battery.bmw.statemachine.StateMachine;
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.battery.protection.BatteryProtection;
+import io.openems.edge.bridge.http.api.BridgeHttp;
+import io.openems.edge.bridge.http.api.BridgeHttp.Endpoint;
+import io.openems.edge.bridge.http.api.BridgeHttpFactory;
+import io.openems.edge.bridge.http.api.HttpMethod;
+import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
+import io.openems.edge.bridge.modbus.api.BridgeModbus;
+import io.openems.edge.bridge.modbus.api.BridgeModbusTcp;
+import io.openems.edge.bridge.modbus.api.ElementToChannelConverter;
+import io.openems.edge.bridge.modbus.api.ModbusComponent;
+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.UnsignedWordElement;
+import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask;
+import io.openems.edge.bridge.modbus.api.task.FC4ReadInputRegistersTask;
+import io.openems.edge.common.channel.Channel;
+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.ModbusSlaveNatureTable;
+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.common.taskmanager.Priority;
+import io.openems.edge.common.type.TypeUtils;
+
+@Designate(ocd = Config.class, factory = true)
+@Component(//
+ name = "Battery.BMW", //
+ immediate = true, //
+ configurationPolicy = ConfigurationPolicy.REQUIRE //
+)
+@EventTopics({ //
+ EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE //
+})
+public class BatteryBmwImpl extends AbstractOpenemsModbusComponent
+ implements ModbusComponent, BatteryBmw, Battery, OpenemsComponent, EventHandler, ModbusSlave, StartStoppable {
+
+ private static final int INNER_RESISTANCE = 200; // [mOhm]
+ private static final double MIN_ALLOWED_SOC = 4d;
+ private static final double MAX_ALLOWED_SOC = 96d;
+ private static final String URI_LOGIN = "login";
+
+ private final Logger log = LoggerFactory.getLogger(BatteryBmwImpl.class);
+ private final StateMachine stateMachine = new StateMachine(State.UNDEFINED);
+
+ private Config config;
+ private BatteryProtection batteryProtection = null;
+ private final AtomicReference startStopTarget = new AtomicReference<>(StartStop.UNDEFINED);
+
+ @Reference
+ private BmwToken token;
+
+ @Reference
+ private ConfigurationAdmin cm;
+
+ @Reference
+ private ComponentManager componentManager;
+
+ @Reference
+ private OpenemsEdgeOem oem;
+
+ @Reference
+ private BridgeHttpFactory httpBridgeFactory;
+ private BridgeHttp httpBridge;
+
+ @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
+ protected void setModbus(BridgeModbus modbus) {
+ super.setModbus(modbus);
+ }
+
+ public BatteryBmwImpl() {
+ super(//
+ OpenemsComponent.ChannelId.values(), //
+ ModbusComponent.ChannelId.values(), //
+ Battery.ChannelId.values(), //
+ StartStoppable.ChannelId.values(), //
+ BatteryProtection.ChannelId.values(), //
+ BatteryBmw.ChannelId.values() //
+ );
+ }
+
+ @Activate
+ private void activate(ComponentContext context, Config config) throws OpenemsNamedException {
+ this.config = config;
+ if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm,
+ "Modbus", config.modbus_id())) {
+ return;
+ }
+ this._setInnerResistance(INNER_RESISTANCE);
+
+ // Initialize Battery-Protection
+ this.batteryProtection = BatteryProtection.create(this) //
+ .applyBatteryProtectionDefinition(new BatteryProtectionDefinition(), this.componentManager) //
+ .build();
+
+ this.httpBridge = this.httpBridgeFactory.get();
+
+ final var url = this.getUrl((BridgeModbusTcp) this.getBridgeModbus(), URI_LOGIN, "");
+
+ var auth = this.oem.getBmwBatteryAuth();
+ if (auth == null) {
+ return;
+ }
+ final var userName = auth.a();
+ final var password = auth.b();
+ var outData = "{userCredentials: {name: \"" + userName + "\" , password: \"" + password + "\"}}";
+ var map = Map.of(//
+ "Content-Type", "application/json");
+ final var endPoint = new Endpoint(url, HttpMethod.POST, BridgeHttp.DEFAULT_CONNECT_TIMEOUT,
+ BridgeHttp.DEFAULT_READ_TIMEOUT, outData, map);
+ this.token.fetchToken(endPoint);
+ }
+
+ public String getToken() {
+ return this.token.getToken();
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ this.httpBridgeFactory.unget(this.httpBridge);
+ this.httpBridge = null;
+ super.deactivate();
+ }
+
+ @Override
+ protected ModbusProtocol defineModbusProtocol() {
+ return new ModbusProtocol(this, //
+ new FC16WriteRegistersTask(1400, //
+ m(BatteryBmw.ChannelId.BATTERY_STATE_COMMAND, new UnsignedWordElement(1400)) //
+ ),
+
+ new FC4ReadInputRegistersTask(1000, Priority.HIGH,
+ m(BatteryBmw.ChannelId.BATTERY_STATE, new UnsignedWordElement(1000)),
+
+ m(new BitsWordElement(1001, this) // ErrBits1
+ .bit(0, BatteryBmw.ChannelId.UNSPECIFIED_ERROR) //
+ .bit(1, BatteryBmw.ChannelId.LOW_VOLTAGE_ERROR) //
+ .bit(2, BatteryBmw.ChannelId.HIGH_VOLTAGE_ERROR) //
+ .bit(3, BatteryBmw.ChannelId.CHARGE_CURRENT_ERROR) //
+ .bit(4, BatteryBmw.ChannelId.DISCHARGE_CURRENT_ERROR) //
+ .bit(5, BatteryBmw.ChannelId.CHARGE_POWER_ERROR) //
+ .bit(6, BatteryBmw.ChannelId.DISCHARGE_POWER_ERROR) //
+ .bit(7, BatteryBmw.ChannelId.LOW_SOC_ERROR) //
+ .bit(8, BatteryBmw.ChannelId.HIGH_SOC_ERROR) //
+ .bit(9, BatteryBmw.ChannelId.LOW_TEMPERATURE_ERROR) //
+ .bit(10, BatteryBmw.ChannelId.HIGH_TEMPERATURE_ERROR) //
+ .bit(11, BatteryBmw.ChannelId.INSULATION_ERROR) //
+ .bit(12, BatteryBmw.ChannelId.CONTACTOR_ERROR) //
+ .bit(13, BatteryBmw.ChannelId.SENSOR_ERROR) //
+ .bit(14, BatteryBmw.ChannelId.IMBALANCE_ERROR) //
+ .bit(15, BatteryBmw.ChannelId.COMMUNICATION_ERROR) //
+
+ ), //
+
+ m(new BitsWordElement(1002, this) // ErrBits2
+ .bit(0, BatteryBmw.ChannelId.CONTAINER_ERROR) //
+ .bit(1, BatteryBmw.ChannelId.SOH_ERROR) //
+ .bit(2, BatteryBmw.ChannelId.RACK_STING_ERROR) //
+ ), //
+
+ m(new BitsWordElement(1003, this) // WarnBits1
+ .bit(0, BatteryBmw.ChannelId.UNSPECIFIED_WARNING) //
+ .bit(1, BatteryBmw.ChannelId.LOW_VOLTAGE_WARNING) //
+ .bit(2, BatteryBmw.ChannelId.HIGH_VOLTAGE_WARNING) //
+ .bit(3, BatteryBmw.ChannelId.CHARGE_CURRENT_WARNING) //
+ .bit(4, BatteryBmw.ChannelId.DISCHARGE_CURRENT_WARNING) //
+ .bit(5, BatteryBmw.ChannelId.CHARGE_POWER_WARNING) //
+ .bit(6, BatteryBmw.ChannelId.DISCHARGE_POWER_WARNING) //
+ .bit(7, BatteryBmw.ChannelId.LOW_SOC_WARNING) //
+ .bit(8, BatteryBmw.ChannelId.HIGH_SOC_WARNING) //
+ .bit(9, BatteryBmw.ChannelId.LOW_TEMPERATURE_WARNING) //
+ .bit(10, BatteryBmw.ChannelId.HIGH_TEMPERATURE_WARNING) //
+ .bit(11, BatteryBmw.ChannelId.INSULATION_WARNING) //
+ .bit(12, BatteryBmw.ChannelId.CONTACTOR_WARNING) //
+ .bit(13, BatteryBmw.ChannelId.SENSOR_WARNING) //
+ .bit(14, BatteryBmw.ChannelId.IMBALANCE_WARNING) //
+ .bit(15, BatteryBmw.ChannelId.COMMUNICATION_WARNING) //
+ ), //
+
+ m(new BitsWordElement(1004, this) // WarnBits2
+ .bit(0, BatteryBmw.ChannelId.CONTAINER_WARNING) //
+ .bit(1, BatteryBmw.ChannelId.SOH_WARNING) //
+ .bit(2, BatteryBmw.ChannelId.RACK_STING_WARNING) //
+ ), //
+
+ new DummyRegisterElement(1005), //
+ m(BatteryBmw.ChannelId.MAX_OPERATING_CURRENT, new SignedWordElement(1006)), //
+ m(BatteryBmw.ChannelId.MIN_OPERATING_CURRENT, new SignedWordElement(1007)), //
+ m(Battery.ChannelId.CHARGE_MAX_VOLTAGE, new SignedWordElement(1008), SCALE_FACTOR_MINUS_1), //
+ m(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE, new SignedWordElement(1009), SCALE_FACTOR_MINUS_1), //
+ m(BatteryProtection.ChannelId.BP_DISCHARGE_BMS, new SignedWordElement(1010)), //
+ m(BatteryProtection.ChannelId.BP_CHARGE_BMS, new SignedWordElement(1011), INVERT),
+ m(BatteryBmw.ChannelId.MAX_DYNAMIC_VOLTAGE, new UnsignedWordElement(1012)),
+ m(BatteryBmw.ChannelId.MIN_DYNAMIC_VOLTAGE, new UnsignedWordElement(1013)),
+ m(BatteryBmw.ChannelId.CONNECTED_STRING_NUMBER, new UnsignedWordElement(1014)), //
+ m(BatteryBmw.ChannelId.INSTALLED_STRING_NUMBER, new UnsignedWordElement(1015)), //
+ m(BatteryBmw.ChannelId.BATTERY_TOTAL_SOC, new UnsignedWordElement(1016)),
+ m(BatteryBmw.ChannelId.BATTERY_SOC, new UnsignedWordElement(1017)),
+ m(BatteryBmw.ChannelId.REMAINING_CHARGE_CAPACITY, new UnsignedWordElement(1018)), //
+ m(BatteryBmw.ChannelId.REMAINING_DISCHARGE_CAPACITY, new UnsignedWordElement(1019)), //
+ m(BatteryBmw.ChannelId.REMANING_CHARGE_ENERGY, new UnsignedWordElement(1020)), //
+ m(BatteryBmw.ChannelId.REMANING_DISCHARGE_ENERGY, new UnsignedWordElement(1021)), //
+ m(BatteryBmw.ChannelId.NOMINAL_ENERGY, new UnsignedWordElement(1022)), //
+ m(BatteryBmw.ChannelId.NOMINAL_ENERGY_TOTAL, new UnsignedWordElement(1023)), //
+ m(BatteryBmw.ChannelId.NOMINAL_CAPACITY, new UnsignedWordElement(1024)), //
+ m(Battery.ChannelId.CAPACITY, new UnsignedWordElement(1025)), //
+ m(Battery.ChannelId.SOH, new UnsignedWordElement(1026), SCALE_FACTOR_MINUS_2), //
+ // TODO battery.api
+ m(BatteryBmw.ChannelId.LINK_VOLTAGE, new UnsignedWordElement(1027)), //
+ m(BatteryBmw.ChannelId.INTERNAL_VOLTAGE, new UnsignedWordElement(1028)), //
+ m(Battery.ChannelId.CURRENT, new SignedWordElement(1029), SCALE_FACTOR_MINUS_1), //
+ m(BatteryBmw.ChannelId.AVG_BATTERY_TEMPERATURE, new UnsignedWordElement(1030),
+ ElementToChannelConverter.SCALE_FACTOR_MINUS_1), //
+ m(Battery.ChannelId.MIN_CELL_TEMPERATURE, new SignedWordElement(1031), SCALE_FACTOR_MINUS_1), //
+ m(Battery.ChannelId.MAX_CELL_TEMPERATURE, new SignedWordElement(1032), SCALE_FACTOR_MINUS_1), //
+ m(Battery.ChannelId.MIN_CELL_VOLTAGE, new SignedWordElement(1033), DIRECT_1_TO_1), //
+ m(Battery.ChannelId.MAX_CELL_VOLTAGE, new SignedWordElement(1034), DIRECT_1_TO_1), //
+ m(BatteryBmw.ChannelId.AVG_CELL_TEMPERATURE, new UnsignedWordElement(1035),
+ ElementToChannelConverter.SCALE_FACTOR_MINUS_2), //
+ new DummyRegisterElement(1036), //
+ m(BatteryBmw.ChannelId.INSULATION_RESISTANCE, new UnsignedWordElement(1037)), //
+ new DummyRegisterElement(1038, 1040), //
+ m(BatteryBmw.ChannelId.DISCHARGE_MAX_CURRENT_HIGH_RESOLUTION, new UnsignedWordElement(1041)), //
+ m(BatteryBmw.ChannelId.CHARGE_MAX_CURRENT_HIGH_RESOLUTION, new UnsignedWordElement(1042)), //
+ m(BatteryBmw.ChannelId.FULL_CYCLE_COUNT, new UnsignedWordElement(1043)) //
+ ));
+ }
+
+ @Override
+ public String debugLog() {
+ return Battery.generateDebugLog(this, this.stateMachine);
+ }
+
+ @Override
+ public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
+ return new ModbusSlaveTable(//
+ OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
+ Battery.getModbusSlaveNatureTable(accessMode), //
+ ModbusSlaveNatureTable.of(BatteryBmw.class, accessMode, 100) //
+ .build());
+ }
+
+ @Override
+ public void handleEvent(Event event) {
+ if (!this.isEnabled()) {
+ return;
+ }
+ switch (event.getTopic()) {
+ case EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE -> {
+ this.batteryProtection.apply();
+ this.handleStateMachine();
+ }
+ }
+ }
+
+ /**
+ * Handles the State-Machine.
+ */
+ private void handleStateMachine() {
+ final var currentState = this.stateMachine.getCurrentState();
+ this._setStateMachine(currentState);
+
+ // Initialize 'Start-Stop' Channel
+ this._setStartStop(StartStop.UNDEFINED);
+
+ try {
+ var context = new Context(this, this.componentManager.getClock(), this.httpBridge);
+ this.stateMachine.run(context);
+ this._setRunFailed(false);
+ } catch (OpenemsNamedException e) {
+ this._setRunFailed(true);
+ this.logError(this.log, "StateMachine failed: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void setStartStop(StartStop value) {
+ if (this.startStopTarget.getAndSet(value) != value) {
+ this.stateMachine.forceNextState(State.UNDEFINED);
+ }
+ }
+
+ @Override
+ public StartStop getStartStopTarget() {
+ return switch (this.config.startStop()) {
+ case AUTO -> this.startStopTarget.get();
+ case START -> StartStop.START;
+ case STOP -> StartStop.STOP;
+ };
+ }
+
+ protected synchronized void updateSoc() {
+ Channel batterySocChannel = this.channel(BatteryBmw.ChannelId.BATTERY_SOC);
+ var batterySoc = batterySocChannel.value();
+ var soc = batterySoc.asOptional().map(t -> {
+ var calculatedBatterySoc = (int) ((t / 100.0 - MIN_ALLOWED_SOC)
+ * (100.0 / (MAX_ALLOWED_SOC - MIN_ALLOWED_SOC)) * 10.0);
+ this._setSocRawValue(calculatedBatterySoc);
+ if (calculatedBatterySoc < 0) {
+ return 0;
+ }
+ if (calculatedBatterySoc > 1000) {
+ return 100;
+ }
+ return calculatedBatterySoc / 10;
+ }).orElse(null);
+ this._setSoc(soc);
+ }
+
+ /**
+ * The internal voltage is preferred when the battery is started, as the
+ * internal voltage is more accurate than the junction voltage.
+ */
+ protected synchronized void updateVoltage() {
+ Integer batteryVoltage = null;
+ if (this.getInternalVoltage().isDefined() && this.getLinkVoltage().isDefined()) {
+ if (this.isStarted()) {
+ batteryVoltage = this.getInternalVoltage().get();
+ } else {
+ batteryVoltage = this.getLinkVoltage().get();
+ }
+ }
+ this._setVoltage(TypeUtils.divide(batteryVoltage, 10));
+ }
+
+ /**
+ * Start the battery.
+ */
+ public void startBattery() {
+ try {
+ this.setBatteryStateCommand(BatteryStateCommand.CLOSE_CONTACTOR);
+ } catch (OpenemsNamedException e) {
+ this.logError(this.log, "Battery can not start : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Stop the battery.
+ */
+ public void stopBattery() {
+ try {
+ this.setBatteryStateCommand(BatteryStateCommand.OPEN_CONTACTOR);
+ } catch (OpenemsNamedException e) {
+ this.logError(this.log, "Battery can not stop : " + e.getMessage());
+ }
+ }
+
+ /**
+ * Check if battery is in running state.
+ *
+ * @return true if battery is started
+ */
+ public boolean isRunning() {
+ return this.getBatteryState().asEnum() == BatteryState.OPERATION;
+ }
+
+ /**
+ * Check if battery is in stopped state.
+ *
+ * @return true if battery is stopped
+ */
+ public boolean isShutdown() {
+ final var batterySystemState = this.getBatteryState().asEnum();
+ return batterySystemState == BatteryState.READY || batterySystemState == BatteryState.OFF
+ || batterySystemState == BatteryState.UNDEFINED;
+ }
+
+ /**
+ * Gets the URL from ip address, uri and battery id.
+ *
+ * @param bridge the modbus bridge
+ * @param uri the uri
+ * @param id the battery id
+ * @return the url
+ */
+ public String getUrl(BridgeModbusTcp bridge, String uri, String id) {
+ return "http://" + bridge.getIpAddress().getHostAddress() + "/" + uri + "/" + id;
+ }
+
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryProtectionDefinition.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryProtectionDefinition.java
new file mode 100644
index 00000000000..d2a3a99f50a
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BatteryProtectionDefinition.java
@@ -0,0 +1,94 @@
+package io.openems.edge.battery.bmw;
+
+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 BatteryProtectionDefinition implements io.openems.edge.battery.protection.BatteryProtectionDefinition {
+ @Override
+ public int getInitialBmsMaxEverChargeCurrent() {
+ return 135; // [A]
+ }
+
+ @Override
+ public int getInitialBmsMaxEverDischargeCurrent() {
+ return 135; // [A]
+ }
+
+ // Over voltage Protection
+ @Override
+ public PolyLine getChargeVoltageToPercent() {
+ return PolyLine.create() //
+ .addPoint(3304, 1) //
+ .addPoint(Math.nextUp(3304), 1) //
+ .addPoint(4160, 1) //
+ .addPoint(4164, 0.2) //
+ .addPoint(4174, 0.05) //
+ .addPoint(Math.nextDown(4180), 0.01) //
+ .addPoint(4180, 0) //
+ .build();
+ }
+
+ // Low Voltage protection
+ @Override
+ public PolyLine getDischargeVoltageToPercent() {
+ return PolyLine.create() //
+ .addPoint(3304, 0) //
+ .addPoint(Math.nextUp(3308), 0.01) //
+ .addPoint(3314, 0.1) //
+ .addPoint(3340, 1) //
+ .addPoint(4180, 1) //
+ .addPoint(Math.nextUp(4180), 1) //
+ .build();
+ }
+
+ @Override
+ public PolyLine getChargeTemperatureToPercent() {
+ return PolyLine.create() //
+ .addPoint(-20, 0.01) //
+ .addPoint(Math.nextUp(-20), 0.01) //
+ .addPoint(25, 1) //
+ .addPoint(35, 1) //
+ .addPoint(Math.nextDown(44), 0.01) //
+ .addPoint(45, 0) //
+ .build();
+ }
+
+ @Override
+ public PolyLine getDischargeTemperatureToPercent() {
+ return PolyLine.create() //
+ .addPoint(-36, 0.01) //
+ .addPoint(Math.nextUp(-36), 0.01) //
+ .addPoint(5, 1) //
+ .addPoint(35, 1) //
+ .addPoint(Math.nextDown(44), 0.01) //
+ .addPoint(45, 0) //
+ .build();
+ }
+
+ @Override
+ public ForceDischarge.Params getForceDischargeParams() {
+ return new ForceDischarge.Params(4186, 4183, 4180);
+ }
+
+ @Override
+ public ForceCharge.Params getForceChargeParams() {
+ return new ForceCharge.Params(3296, 3298, 3300);
+
+ }
+
+ @Override
+ public Double getMaxIncreaseAmperePerSecond() {
+ return 1.; // [A] per second
+ }
+
+ @Override
+ public PolyLine getChargeSocToPercent() {
+ return PolyLine.empty();
+ }
+
+ @Override
+ public PolyLine getDischargeSocToPercent() {
+ return PolyLine.empty();
+ }
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBattery.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBattery.java
deleted file mode 100644
index b5afa188d90..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBattery.java
+++ /dev/null
@@ -1,288 +0,0 @@
-package io.openems.edge.battery.bmw;
-
-import org.osgi.service.event.EventHandler;
-
-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.battery.bmw.enums.BmsState;
-import io.openems.edge.battery.bmw.enums.ErrorBits1;
-import io.openems.edge.battery.bmw.enums.ErrorBits2;
-import io.openems.edge.battery.bmw.enums.InfoBits;
-import io.openems.edge.battery.bmw.enums.State;
-import io.openems.edge.battery.bmw.enums.WarningBits1;
-import io.openems.edge.battery.bmw.enums.WarningBits2;
-import io.openems.edge.common.channel.BooleanDoc;
-import io.openems.edge.common.channel.Doc;
-import io.openems.edge.common.channel.IntegerDoc;
-import io.openems.edge.common.component.OpenemsComponent;
-
-public interface BmwBattery extends OpenemsComponent, EventHandler {
-
- public static enum ChannelId implements io.openems.edge.common.channel.ChannelId {
-
- HEART_BEAT_DEBUG(Doc.of(OpenemsType.INTEGER)), //
- HEART_BEAT(new IntegerDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.HEART_BEAT_DEBUG)),
-
- BMS_STATE_COMMAND(Doc.of(OpenemsType.INTEGER).accessMode(AccessMode.READ_WRITE)), //
-
- BMS_STATE_COMMAND_RESET_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- BMS_STATE_COMMAND_RESET(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.BMS_STATE_COMMAND_RESET_DEBUG)),
-
- BMS_STATE_COMMAND_CLEAR_ERROR_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- BMS_STATE_COMMAND_CLEAR_ERROR(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.BMS_STATE_COMMAND_CLEAR_ERROR_DEBUG)),
-
- BMS_STATE_COMMAND_CLOSE_PRECHARGE_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- BMS_STATE_COMMAND_CLOSE_PRECHARGE(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.BMS_STATE_COMMAND_CLOSE_PRECHARGE_DEBUG)),
-
- BMS_STATE_COMMAND_ERROR_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- BMS_STATE_COMMAND_ERROR(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.BMS_STATE_COMMAND_ERROR_DEBUG)),
-
- BMS_STATE_COMMAND_CLOSE_CONTACTOR_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- BMS_STATE_COMMAND_CLOSE_CONTACTOR(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.BMS_STATE_COMMAND_CLOSE_CONTACTOR_DEBUG)),
-
- BMS_STATE_COMMAND_WAKE_UP_FROM_STOP_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- BMS_STATE_COMMAND_WAKE_UP_FROM_STOP(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.BMS_STATE_COMMAND_WAKE_UP_FROM_STOP_DEBUG)),
-
- BMS_STATE_COMMAND_ENABLE_BATTERY_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- BMS_STATE_COMMAND_ENABLE_BATTERY(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.BMS_STATE_COMMAND_ENABLE_BATTERY_DEBUG)),
-
- OPERATING_STATE_INVERTER_DEBUG(Doc.of(OpenemsType.INTEGER)), //
- OPERATING_STATE_INVERTER(new IntegerDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.OPERATING_STATE_INVERTER_DEBUG)),
-
- DC_LINK_VOLTAGE_DEBUG(Doc.of(OpenemsType.INTEGER)), //
- DC_LINK_VOLTAGE(new IntegerDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .unit(Unit.VOLT).onChannelSetNextWriteMirrorToDebugChannel(ChannelId.DC_LINK_VOLTAGE_DEBUG)),
-
- DC_LINK_CURRENT_DEBUG(Doc.of(OpenemsType.INTEGER)), //
- DC_LINK_CURRENT(new IntegerDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.DC_LINK_VOLTAGE_DEBUG)),
-
- OPERATION_MODE_REQUEST_GRANTED_DEBUG(Doc.of(OpenemsType.INTEGER)), //
- OPERATION_MODE_REQUEST_GRANTED(new IntegerDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.OPERATION_MODE_REQUEST_GRANTED_DEBUG)),
-
- OPERATION_MODE_REQUEST_CANCELED_DEBUG(Doc.of(OpenemsType.INTEGER)), //
- OPERATION_MODE_REQUEST_CANCELED(new IntegerDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.OPERATION_MODE_REQUEST_CANCELED_DEBUG)),
-
- CONNECTION_STRATEGY_HIGH_SOC_FIRST_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- CONNECTION_STRATEGY_HIGH_SOC_FIRST(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.CONNECTION_STRATEGY_HIGH_SOC_FIRST_DEBUG)),
-
- CONNECTION_STRATEGY_LOW_SOC_FIRST_DEBUG(Doc.of(OpenemsType.BOOLEAN)), //
- CONNECTION_STRATEGY_LOW_SOC_FIRST(new BooleanDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.CONNECTION_STRATEGY_LOW_SOC_FIRST_DEBUG)),
-
- SYSTEM_TIME_DEBUG(Doc.of(OpenemsType.INTEGER)), //
- SYSTEM_TIME(new IntegerDoc() //
- .accessMode(AccessMode.READ_WRITE) //
- .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.SYSTEM_TIME_DEBUG)),
-
- // Read only channels
- LIFE_SIGN(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- BMS_STATE(Doc.of(BmsState.values()) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- ERROR_BITS_1(Doc.of(ErrorBits1.values()) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- ERROR_BITS_2(Doc.of(ErrorBits2.values()) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- WARNING_BITS_1(Doc.of(WarningBits1.values()) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- WARNING_BITS_2(Doc.of(WarningBits2.values()) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- INFO_BITS(Doc.of(InfoBits.values()) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- MAXIMUM_OPERATING_CURRENT(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.AMPERE)), //
-
- MINIMUM_OPERATING_CURRENT(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.AMPERE)), //
-
- MAXIMUM_OPERATING_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.VOLT)), //
-
- MINIMUM_OPERATING_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.VOLT)), //
-
- MAXIMUM_LIMIT_DYNAMIC_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.VOLT)), //
-
- MINIMUM_LIMIT_DYNAMIC_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.VOLT)), //
-
- NUMBER_OF_STRINGS_CONNECTED(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- NUMBER_OF_STRINGS_INSTALLED(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- SOC_ALL_STRINGS(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.PERCENT)), //
-
- SOC_CONNECTED_STRINGS(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.PERCENT)), //
-
- REMAINING_CHARGE_CAPACITY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.AMPERE_HOURS)), //
-
- REMAINING_DISCHARGE_CAPACITY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.AMPERE_HOURS)), //
-
- REMAINING_CHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.KILOWATT_HOURS)), //
-
- REMAINING_DISCHARGE_ENERGY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.KILOWATT_HOURS)), //
-
- NOMINAL_ENERGY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.KILOWATT_HOURS)), //
-
- TOTAL_ENERGY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.KILOWATT_HOURS)), //
-
- NOMINAL_CAPACITY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.AMPERE_HOURS)), //
-
- TOTAL_CAPACITY(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.AMPERE_HOURS)), //
-
- DC_VOLTAGE_CONNECTED_RACKS(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.VOLT)), //
-
- DC_VOLTAGE_AVERAGE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.VOLT)), //
-
- DC_CURRENT(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.MILLIAMPERE)), //
-
- AVERAGE_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.DEZIDEGREE_CELSIUS)), //
-
- MINIMUM_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.DEZIDEGREE_CELSIUS)), //
-
- MAXIMUM_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.DEZIDEGREE_CELSIUS)), //
-
- AVERAGE_CELL_VOLTAGE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.MILLIVOLT)), //
-
- INTERNAL_RESISTANCE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.MILLIOHM)), //
-
- INSULATION_RESISTANCE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.KILOOHM)), //
-
- CONTAINER_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.DEZIDEGREE_CELSIUS)), //
-
- AMBIENT_TEMPERATURE(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.DEZIDEGREE_CELSIUS)), //
-
- HUMIDITY_CONTAINER(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.PERCENT)), //
-
- MAXIMUM_LIMIT_DYNAMIC_CURRENT_HIGH_RES(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.MILLIAMPERE)), //
-
- MINIMUM_LIMIT_DYNAMIC_CURRENT_HIGH_RES(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.MILLIAMPERE)), //
-
- FULL_CYCLE_COUNT(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- OPERATING_TIME_COUNT(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY) //
- .unit(Unit.HOUR)), //
-
- COM_PRO_VERSION(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- SERIAL_NUMBER(Doc.of(OpenemsType.INTEGER) //
- .persistencePriority(PersistencePriority.HIGH) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- SOFTWARE_VERSION(Doc.of(OpenemsType.INTEGER) //
- .accessMode(AccessMode.READ_ONLY)), //
-
- STATE_MACHINE(Doc.of(State.values()) //
- .accessMode(AccessMode.READ_ONLY)), //
- ;
-
- private final Doc doc;
-
- private ChannelId(Doc doc) {
- this.doc = doc;
- }
-
- @Override
- public Doc doc() {
- return this.doc;
- }
-
- }
-}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBatteryImpl.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBatteryImpl.java
deleted file mode 100644
index 7c660f82094..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBatteryImpl.java
+++ /dev/null
@@ -1,458 +0,0 @@
-package io.openems.edge.battery.bmw;
-
-import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.DIRECT_1_TO_1;
-import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.INVERT;
-import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2;
-import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
-import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_2;
-
-import java.time.LocalDateTime;
-
-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.EventHandler;
-import org.osgi.service.event.propertytypes.EventTopics;
-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.battery.bmw.enums.BmsState;
-import io.openems.edge.battery.bmw.enums.State;
-import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
-import io.openems.edge.bridge.modbus.api.BridgeModbus;
-import io.openems.edge.bridge.modbus.api.ModbusComponent;
-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.SignedWordElement;
-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.FC4ReadInputRegistersTask;
-import io.openems.edge.common.channel.BooleanWriteChannel;
-import io.openems.edge.common.channel.EnumReadChannel;
-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.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.common.taskmanager.Priority;
-
-@Designate(ocd = Config.class, factory = true)
-@Component(//
- name = "Bmw.Battery", //
- immediate = true, //
- configurationPolicy = ConfigurationPolicy.REQUIRE //
-)
-@EventTopics({ //
- EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE //
-})
-public class BmwBatteryImpl extends AbstractOpenemsModbusComponent
- implements BmwBattery, Battery, ModbusComponent, OpenemsComponent, EventHandler, ModbusSlave, StartStoppable {
-
- private static final Integer OPEN_CONTACTORS = 0;
- private static final Integer CLOSE_CONTACTORS = 4;
-
- private final Logger log = LoggerFactory.getLogger(BmwBatteryImpl.class);
-
- @Reference
- private ConfigurationAdmin cm;
-
- @Override
- @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
- protected void setModbus(BridgeModbus modbus) {
- super.setModbus(modbus);
- }
-
- private State state = State.UNDEFINED;
- private Config config;
- private LocalDateTime errorDelayIsOver = null;
- private int unsuccessfulStarts = 0;
- private LocalDateTime startAttemptTime = null;
- private LocalDateTime pendingTimestamp;
-
- public BmwBatteryImpl() {
- super(//
- OpenemsComponent.ChannelId.values(), //
- ModbusComponent.ChannelId.values(), //
- Battery.ChannelId.values(), //
- StartStoppable.ChannelId.values(), //
- BmwBattery.ChannelId.values() //
- );
- }
-
- @Activate
- private void activate(ComponentContext context, Config config) throws OpenemsNamedException {
- this.config = config;
- if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm,
- "Modbus", config.modbus_id())) {
- return;
- }
- }
-
- @Override
- @Deactivate
- protected void deactivate() {
- super.deactivate();
- }
-
- private void handleStateMachine() {
- var readyForWorking = false;
- switch (this.getStateMachineState()) {
- case ERROR:
- this.clearError();
- // TODO Reset BMS? anything else?
- this.errorDelayIsOver = LocalDateTime.now().plusSeconds(this.config.errorDelay());
- this.setStateMachineState(State.ERRORDELAY);
- break;
- case ERRORDELAY:
- if (LocalDateTime.now().isAfter(this.errorDelayIsOver)) {
- this.errorDelayIsOver = null;
- if (this.isError()) {
- this.setStateMachineState(State.ERROR);
- } else {
- this.setStateMachineState(State.OFF);
- }
- }
- break;
- case INIT:
- if (this.isSystemRunning()) {
- this.setStateMachineState(State.RUNNING);
- this.unsuccessfulStarts = 0;
- this.startAttemptTime = null;
- } else if (this.startAttemptTime.plusSeconds(this.config.maxStartTime()).isBefore(LocalDateTime.now())) {
- this.startAttemptTime = null;
- this.unsuccessfulStarts++;
- this.stopSystem();
- this.setStateMachineState(State.STOPPING);
- if (this.unsuccessfulStarts >= this.config.maxStartAttempts()) {
- this.errorDelayIsOver = LocalDateTime.now().plusSeconds(this.config.startUnsuccessfulDelay());
- this.setStateMachineState(State.ERRORDELAY);
- this.unsuccessfulStarts = 0;
- }
- }
- break;
- case OFF:
- this.logDebug(this.log, "in case 'OFF'; try to start the system");
- this.startSystem();
- this.logDebug(this.log, "set state to 'INIT'");
- this.setStateMachineState(State.INIT);
- this.startAttemptTime = LocalDateTime.now();
- break;
- case RUNNING:
- if (this.isError()) {
- this.setStateMachineState(State.ERROR);
- } else if (!this.isSystemRunning()) {
- this.setStateMachineState(State.UNDEFINED);
- } else {
- this.setStateMachineState(State.RUNNING);
- readyForWorking = true;
- }
- break;
- case STOPPING:
- if (this.isError()) {
- this.setStateMachineState(State.ERROR);
- } else if (this.isSystemStopped()) {
- this.setStateMachineState(State.OFF);
- }
- break;
- case UNDEFINED:
- if (this.isError()) {
- this.setStateMachineState(State.ERROR);
- } else if (this.isSystemStopped()) {
- this.setStateMachineState(State.OFF);
- } else if (this.isSystemRunning()) {
- this.setStateMachineState(State.RUNNING);
- } else if (this.isSystemStatePending()) {
- this.setStateMachineState(State.PENDING);
- }
- break;
- case PENDING:
- if (this.pendingTimestamp == null) {
- this.pendingTimestamp = LocalDateTime.now();
- }
- if (this.pendingTimestamp.plusSeconds(this.config.pendingTolerance()).isBefore(LocalDateTime.now())) {
- // System state could not be determined, stop and start it
- this.pendingTimestamp = null;
- this.stopSystem();
- this.setStateMachineState(State.OFF);
- } else if (this.isError()) {
- this.setStateMachineState(State.ERROR);
- this.pendingTimestamp = null;
- } else if (this.isSystemStopped()) {
- this.setStateMachineState(State.OFF);
- this.pendingTimestamp = null;
- } else if (this.isSystemRunning()) {
- this.setStateMachineState(State.RUNNING);
- this.pendingTimestamp = null;
- }
- break;
- case STANDBY:
- break;
- }
-
- // this.getReadyForWorking().setNextValue(readyForWorking);
- if (readyForWorking) {
- this._setStartStop(StartStop.START);
- } else {
- this._setStartStop(StartStop.STOP);
- }
- }
-
- private void clearError() {
- BooleanWriteChannel clearErrorChannel = this.channel(BmwBattery.ChannelId.BMS_STATE_COMMAND_CLEAR_ERROR);
- try {
- clearErrorChannel.setNextWriteValue(true);
- } catch (OpenemsNamedException e) {
- // TODO should Fault state channel, but after start stop feature
- this.logError(this.log, "Error while trying to reset the system!");
- }
- }
-
- @Override
- public void handleEvent(Event event) {
- if (!this.isEnabled()) {
- return;
- }
- switch (event.getTopic()) {
- case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE:
- this.handleBatteryState();
- break;
- }
- }
-
- private void handleBatteryState() {
- switch (this.config.batteryState()) {
- case DEFAULT:
- this.handleStateMachine();
- break;
- case OFF:
- this.stopSystem();
- break;
- case ON:
- this.startSystem();
- break;
- }
- }
-
- // TODO Check this needed or not
- // public void shutDownBattery() {
- // SymmetricEss ess;
- // try {
- // ess = this.manager.getComponent(this.config.Inverter_id());
- // } catch (OpenemsNamedException e1) {
- //
- // e1.printStackTrace();
- // return;
- // }
- // int activePowerInverter = ess.getActivePower().value().orElse(0);
- // int reactivePowerInverter = ess.getReactivePower().value().orElse(0);
- //
- // if (activePowerInverter == 0 && reactivePowerInverter == 0) {
- // IntegerWriteChannel commandChannel =
- // this.channel(BMWChannelId.BMS_STATE_COMMAND);
- // try {
- // commandChannel.setNextWriteValue(OPEN_CONTACTORS);
- // } catch (OpenemsNamedException e) {
- // log.error("Problem occurred during send start command");
- // }
- // }
- //
- // }
-
- private boolean isSystemRunning() {
- EnumReadChannel bmsStateChannel = this.channel(BmwBattery.ChannelId.BMS_STATE);
- BmsState bmsState = bmsStateChannel.value().asEnum();
- return bmsState == BmsState.OPERATION;
- }
-
- private boolean isSystemStopped() {
- EnumReadChannel bmsStateChannel = this.channel(BmwBattery.ChannelId.BMS_STATE);
- BmsState bmsState = bmsStateChannel.value().asEnum();
- return bmsState == BmsState.OFF;
- }
-
- /**
- * Checks whether system has an undefined state.
- *
- * @return true if system is neither running nor stopped
- */
- private boolean isSystemStatePending() {
- return !this.isSystemRunning() && !this.isSystemStopped();
- }
-
- private boolean isError() {
- EnumReadChannel bmsStateChannel = this.channel(BmwBattery.ChannelId.BMS_STATE);
- BmsState bmsState = bmsStateChannel.value().asEnum();
- return bmsState == BmsState.ERROR;
- }
-
- @Override
- public String debugLog() {
- return "SoC:" + this.getSoc() //
- + "|Discharge:" + this.getDischargeMinVoltage() + ";" + this.getDischargeMaxCurrent() //
- + "|Charge:" + this.getChargeMaxVoltage() + ";" + this.getChargeMaxCurrent() //
- + "|State:" + this.state.asCamelCase();
- }
-
- private void startSystem() {
- // TODO Currently not necessary, Battery starts itself?!
- this.log.debug("Start system");
- IntegerWriteChannel commandChannel = this.channel(BmwBattery.ChannelId.BMS_STATE_COMMAND);
- try {
- commandChannel.setNextWriteValue(CLOSE_CONTACTORS);
- } catch (OpenemsNamedException e) {
- // TODO Auto-generated catch block
- this.logError(this.log, "Problem occurred during send start command");
- }
- }
-
- private void stopSystem() {
- // TODO Currently not necessary, Battery starts itself?!
- this.log.debug("Stop system");
- IntegerWriteChannel commandChannel = this.channel(BmwBattery.ChannelId.BMS_STATE_COMMAND);
- try {
- commandChannel.setNextWriteValue(OPEN_CONTACTORS);
- } catch (OpenemsNamedException e) {
- this.logError(this.log, "Problem occurred during send stopping command");
- }
- }
-
- private State getStateMachineState() {
- return this.state;
- }
-
- private void setStateMachineState(State state) {
- this.state = state;
- this.channel(BmwBattery.ChannelId.STATE_MACHINE).setNextValue(this.state);
- }
-
- @Override
- protected ModbusProtocol defineModbusProtocol() {
- return new ModbusProtocol(this, //
- new FC16WriteRegistersTask(1399, //
- m(BmwBattery.ChannelId.HEART_BEAT, new UnsignedWordElement(1399)), //
- m(BmwBattery.ChannelId.BMS_STATE_COMMAND, new UnsignedWordElement(1400)), //
- m(BmwBattery.ChannelId.OPERATING_STATE_INVERTER, new UnsignedWordElement(1401)), //
- m(BmwBattery.ChannelId.DC_LINK_VOLTAGE, new UnsignedWordElement(1402), SCALE_FACTOR_MINUS_1), //
- m(BmwBattery.ChannelId.DC_LINK_CURRENT, new UnsignedWordElement(1403)), //
- m(BmwBattery.ChannelId.OPERATION_MODE_REQUEST_GRANTED, new UnsignedWordElement(1404)), //
- m(BmwBattery.ChannelId.OPERATION_MODE_REQUEST_CANCELED, new UnsignedWordElement(1405)), //
- m(new BitsWordElement(1406, this) //
- .bit(1, BmwBattery.ChannelId.CONNECTION_STRATEGY_HIGH_SOC_FIRST) //
- .bit(0, BmwBattery.ChannelId.CONNECTION_STRATEGY_LOW_SOC_FIRST) //
- ), //
- m(BmwBattery.ChannelId.SYSTEM_TIME, new UnsignedDoublewordElement(1407)) //
- ),
-
- new FC4ReadInputRegistersTask(999, Priority.HIGH,
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.LIFE_SIGN, new UnsignedWordElement(999)),
- m(BmwBattery.ChannelId.BMS_STATE, new UnsignedWordElement(1000)), //
- m(BmwBattery.ChannelId.ERROR_BITS_1, new UnsignedWordElement(1001)), //
- m(BmwBattery.ChannelId.ERROR_BITS_2, new UnsignedWordElement(1002)), //
- m(BmwBattery.ChannelId.WARNING_BITS_1, new UnsignedWordElement(1003)), //
- m(BmwBattery.ChannelId.WARNING_BITS_2, new UnsignedWordElement(1004)), //
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.INFO_BITS, new UnsignedWordElement(1005)),
- m(BmwBattery.ChannelId.MAXIMUM_OPERATING_CURRENT, new SignedWordElement(1006)), //
- m(BmwBattery.ChannelId.MINIMUM_OPERATING_CURRENT, new SignedWordElement(1007)), //
- m(Battery.ChannelId.CHARGE_MAX_VOLTAGE, new UnsignedWordElement(1008), SCALE_FACTOR_MINUS_1), //
- m(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE, new UnsignedWordElement(1009), SCALE_FACTOR_MINUS_1), //
- m(Battery.ChannelId.DISCHARGE_MAX_CURRENT, new SignedWordElement(1010)), //
- m(Battery.ChannelId.CHARGE_MAX_CURRENT, new SignedWordElement(1011), INVERT), //
- m(BmwBattery.ChannelId.MAXIMUM_LIMIT_DYNAMIC_VOLTAGE, new UnsignedWordElement(1012),
- SCALE_FACTOR_MINUS_1), //
- m(BmwBattery.ChannelId.MINIMUM_LIMIT_DYNAMIC_VOLTAGE, new UnsignedWordElement(1013),
- SCALE_FACTOR_MINUS_1), //
- m(BmwBattery.ChannelId.NUMBER_OF_STRINGS_CONNECTED, new UnsignedWordElement(1014)), //
- m(BmwBattery.ChannelId.NUMBER_OF_STRINGS_INSTALLED, new UnsignedWordElement(1015)), //
- m(BmwBattery.ChannelId.SOC_ALL_STRINGS, new UnsignedWordElement(1016), SCALE_FACTOR_MINUS_2), //
- m(Battery.ChannelId.SOC, new UnsignedWordElement(1017), SCALE_FACTOR_MINUS_2), //
- m(BmwBattery.ChannelId.REMAINING_CHARGE_CAPACITY, new UnsignedWordElement(1018)), //
- m(BmwBattery.ChannelId.REMAINING_DISCHARGE_CAPACITY, new UnsignedWordElement(1019)), //
- m(BmwBattery.ChannelId.REMAINING_CHARGE_ENERGY, new UnsignedWordElement(1020)), //
- m(BmwBattery.ChannelId.REMAINING_DISCHARGE_ENERGY, new UnsignedWordElement(1021)), //
- m(BmwBattery.ChannelId.NOMINAL_ENERGY, new UnsignedWordElement(1022)), //
- m(BmwBattery.ChannelId.TOTAL_ENERGY, new UnsignedWordElement(1023)), //
- m(BmwBattery.ChannelId.NOMINAL_CAPACITY, new UnsignedWordElement(1024)), //
- m(Battery.ChannelId.CAPACITY, new UnsignedWordElement(1025)), //
- m(Battery.ChannelId.SOH, new UnsignedWordElement(1026), SCALE_FACTOR_MINUS_2), //
- m(Battery.ChannelId.VOLTAGE, new UnsignedWordElement(1027), SCALE_FACTOR_MINUS_1), //
- m(BmwBattery.ChannelId.DC_VOLTAGE_AVERAGE, new UnsignedWordElement(1028),
- SCALE_FACTOR_MINUS_1)), //
- new FC4ReadInputRegistersTask(1029, Priority.HIGH, //
- m(new SignedWordElement(1029)) //
- .m(BmwBattery.ChannelId.DC_CURRENT, SCALE_FACTOR_MINUS_1) //
- .m(Battery.ChannelId.CURRENT, SCALE_FACTOR_MINUS_1) //
- .build()), //
- new FC4ReadInputRegistersTask(1030, Priority.HIGH, //
- m(BmwBattery.ChannelId.AVERAGE_TEMPERATURE, new SignedWordElement(1030))), //
- new FC4ReadInputRegistersTask(1031, Priority.HIGH, //
- m(new SignedWordElement(1031)) //
- .m(BmwBattery.ChannelId.MINIMUM_TEMPERATURE, DIRECT_1_TO_1) //
- .m(Battery.ChannelId.MIN_CELL_TEMPERATURE, DIRECT_1_TO_1) //
- .build()), //
- new FC4ReadInputRegistersTask(1032, Priority.HIGH, //
- m(new SignedWordElement(1032)) //
- .m(BmwBattery.ChannelId.MAXIMUM_TEMPERATURE, DIRECT_1_TO_1) //
- .m(Battery.ChannelId.MAX_CELL_TEMPERATURE, DIRECT_1_TO_1) //
- .build()), //
- new FC4ReadInputRegistersTask(1033, Priority.HIGH,
- m(Battery.ChannelId.MIN_CELL_VOLTAGE, new UnsignedWordElement(1033)), //
- m(Battery.ChannelId.MAX_CELL_VOLTAGE, new UnsignedWordElement(1034)), //
- m(BmwBattery.ChannelId.AVERAGE_CELL_VOLTAGE, new UnsignedWordElement(1035)), //
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.INTERNAL_RESISTANCE, new UnsignedWordElement(1036)),
- m(BmwBattery.ChannelId.INSULATION_RESISTANCE, new UnsignedWordElement(1037), DIRECT_1_TO_1), //
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.CONTAINER_TEMPERATURE, new UnsignedWordElement(1038),
- SCALE_FACTOR_MINUS_1),
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.AMBIENT_TEMPERATURE, new UnsignedWordElement(1039),
- SCALE_FACTOR_MINUS_1),
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.HUMIDITY_CONTAINER, new UnsignedWordElement(1040), SCALE_FACTOR_MINUS_1),
- m(BmwBattery.ChannelId.MAXIMUM_LIMIT_DYNAMIC_CURRENT_HIGH_RES, new SignedWordElement(1041),
- SCALE_FACTOR_2), //
- m(BmwBattery.ChannelId.MINIMUM_LIMIT_DYNAMIC_CURRENT_HIGH_RES, new SignedWordElement(1042),
- SCALE_FACTOR_2), //
- m(BmwBattery.ChannelId.FULL_CYCLE_COUNT, new UnsignedWordElement(1043)), //
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.OPERATING_TIME_COUNT, new UnsignedDoublewordElement(1044)),
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.COM_PRO_VERSION, new UnsignedDoublewordElement(1046)),
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.SERIAL_NUMBER, new UnsignedDoublewordElement(1048)),
- // not defined by "BCS_HL-SW_Operating-Instructions_V1.0.2_under_work_ChL.pdf"
- m(BmwBattery.ChannelId.SOFTWARE_VERSION, new UnsignedDoublewordElement(1050)) //
- ));
- }
-
- @Override
- public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
- return new ModbusSlaveTable(//
- OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
- Battery.getModbusSlaveNatureTable(accessMode) //
- );
- }
-
- @Override
- public void setStartStop(StartStop value) throws OpenemsNamedException {
- // TODO Auto-generated method stub
- }
-}
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwToken.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwToken.java
new file mode 100644
index 00000000000..843ccae9eee
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwToken.java
@@ -0,0 +1,71 @@
+package io.openems.edge.battery.bmw;
+
+import java.time.Duration;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceScope;
+import org.osgi.service.component.annotations.ServiceScope;
+
+import io.openems.common.types.HttpStatus;
+import io.openems.edge.bridge.http.api.BridgeHttp;
+import io.openems.edge.bridge.http.api.BridgeHttp.Endpoint;
+import io.openems.edge.bridge.http.api.HttpError;
+import io.openems.edge.bridge.http.api.HttpResponse;
+import io.openems.edge.bridge.http.time.DelayTimeProvider;
+import io.openems.edge.bridge.http.time.DelayTimeProviderChain;
+
+@Component(scope = ServiceScope.SINGLETON, service = BmwToken.class)
+public class BmwToken {
+ private static final int FETCH_TOKEN_DELAY = 30;
+
+ private String token;
+ private BridgeHttp http;
+
+ @Activate
+ public BmwToken(@Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED) BridgeHttp http) {
+ this.http = http;
+ }
+
+ /**
+ * Fetch the token.
+ *
+ * @param endpoint the endpoint
+ */
+ public synchronized void fetchToken(Endpoint endpoint) {
+ // TODO check if the token is expires
+ this.http.subscribeJsonTime(new BmwTokenDelayProvider(), endpoint, (a, b) -> {
+ if (a == null) {
+ return;
+ }
+ var jsonObject = a.data().getAsJsonObject();
+ this.token = jsonObject.getAsJsonPrimitive("jwtToken").getAsString();
+ });
+ }
+
+ public String getToken() {
+ return this.token;
+ }
+
+ private static final class BmwTokenDelayProvider implements DelayTimeProvider {
+
+ @Override
+ public Delay onFirstRunDelay() {
+ return DelayTimeProviderChain.fixedDelay(Duration.ofSeconds(FETCH_TOKEN_DELAY)).getDelay();
+ }
+
+ @Override
+ public Delay onErrorRunDelay(HttpError error) {
+ return DelayTimeProviderChain.fixedDelay(Duration.ofSeconds(FETCH_TOKEN_DELAY)).getDelay();
+ }
+
+ @Override
+ public Delay onSuccessRunDelay(HttpResponse result) {
+ if (result.status() != HttpStatus.OK) {
+ return DelayTimeProviderChain.fixedDelay(Duration.ofSeconds(FETCH_TOKEN_DELAY)).getDelay();
+ }
+ return DelayTimeProviderChain.runNeverAgain().getDelay();
+ }
+ }
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/Config.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/Config.java
index 1f7c6d2d852..6f8252dff6c 100644
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/Config.java
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/Config.java
@@ -3,15 +3,15 @@
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import io.openems.edge.battery.bmw.enums.BatteryState;
+import io.openems.edge.common.startstop.StartStopConfig;
@ObjectClassDefinition(//
name = "Battery BMW", //
description = "Implements the BMW battery rack system.")
-@interface Config {
+public @interface Config {
@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
- String id() default "bms0";
+ String id() default "battery0";
@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
String alias() default "";
@@ -19,33 +19,17 @@
@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.AUTO;
+
@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 0;
-
- @AttributeDefinition(name = "Battery state", description = "Switches the battery into the given state, if default is used, battery state is set automatically")
- BatteryState batteryState() default BatteryState.DEFAULT;
-
- @AttributeDefinition(name = "Error Delay", description = "When an error occurs, system will remain the given time in error delay state")
- long errorDelay() default 600;
+ int modbusUnitId();
@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "(enabled=true)";
- @AttributeDefinition(name = "Max Start Attempts", description = "Sets the counter how many time the system should try to start")
- int maxStartAttempts() default 5;
-
- @AttributeDefinition(name = "Max Start Time", description = "Max Time in seconds allowed for starting the system")
- int maxStartTime() default 30;
-
- @AttributeDefinition(name = "Start Not Successful Delay Time", description = "Sets the delay time in seconds how long the system should be stopped if it was not able to start")
- int startUnsuccessfulDelay() default 3600;
-
- @AttributeDefinition(name = "Pending Tolerance", description = "time in seconds, that is waited if system status cannot be determined e.g. in case of reading errors")
- int pendingTolerance() default 15;
-
String webconsole_configurationFactory_nameHint() default "Battery BMW [{id}]";
-
}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BatteryState.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BatteryState.java
index 2ea190ab4af..d510f7a35ca 100644
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BatteryState.java
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BatteryState.java
@@ -1,8 +1,38 @@
package io.openems.edge.battery.bmw.enums;
-public enum BatteryState {
- DEFAULT, //
- ON, //
- OFF;
+import io.openems.common.types.OptionsEnum;
-}
+public enum BatteryState implements OptionsEnum {
+ UNDEFINED(-1, "Undefined"), //
+ OFF(0, "Off"), //
+ INIT(2, "Init"), //
+ STANDBY(4, "Standby"), //
+ READY(8, "Ready"), //
+ OPERATION(16, "Operation"), //
+ ERROR(32, "Error"), //
+ PRE_CHARGE(256, "Precharge") //
+ ;
+
+ private final int value;
+ private final String name;
+
+ private BatteryState(int value, String name) {
+ this.value = value;
+ this.name = name;
+ }
+
+ @Override
+ public int getValue() {
+ return this.value;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public OptionsEnum getUndefined() {
+ return UNDEFINED;
+ }
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BatteryStateCommand.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BatteryStateCommand.java
new file mode 100644
index 00000000000..6fcdef77cc1
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BatteryStateCommand.java
@@ -0,0 +1,48 @@
+package io.openems.edge.battery.bmw.enums;
+
+import io.openems.common.types.OptionsEnum;
+
+public enum BatteryStateCommand implements OptionsEnum {
+ UNDEFINED(-1, "Undefined"), //
+ OPEN_CONTACTOR(0, "Open Contactor"), //
+ RESERVED_ENABLE_BATTERY(1, "Reserved Enable Battery"), //
+ RESERVED_OPERATION(2, "Reserved Operation"), //
+ CLOSE_CONTACTOR(4, "Close Contactor"), //
+ CLOSE_PRE_CHARGE(8, "Close Pre-Charge"), //
+ RES_BMS_STT_CMD_BIT_4(16, "Reserved BMS State Command Bit 4"), //
+ RES_BMS_STT_CMD_BIT_5(32, "Reserved BMS State Command Bit 5"), //
+ RES_BMS_STT_CMD_BIT_6(64, "Reserved BMS State Command Bit 6"), //
+ RES_BMS_STT_CMD_BIT_7(128, "Reserved BMS State Command Bit 7"), //
+ RES_BMS_STT_CMD_BIT_8(256, "Reserved BMS State Command Bit 8"), //
+ RES_BMS_STT_CMD_BIT_9(512, "Reserved BMS State Command Bit 9"), //
+ RES_BMS_STT_CMD_BIT_10(1024, "Reserved BMS State Command Bit 10"), //
+ RES_BMS_STT_CMD_BIT_11(2048, "Reserved BMS State Command Bit 11"), //
+ RES_BMS_STT_CMD_BIT_12(4096, "Reserved BMS State Command Bit 12"), //
+ ReservedD_BMS_SLEEP(8192, "Reservedd BMS-Sleep"), //
+ CLEAR_BMS_ERROR(16384, "Clear BMS Error"), //
+ RESET_BMS(32768, "Reset BMS") //
+ ;
+
+ private final int value;
+ private final String name;
+
+ private BatteryStateCommand(int value, String name) {
+ this.value = value;
+ this.name = 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.bmw/src/io/openems/edge/battery/bmw/enums/BmsState.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BmsState.java
deleted file mode 100644
index d6448af0836..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BmsState.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package io.openems.edge.battery.bmw.enums;
-
-import io.openems.common.types.OptionsEnum;
-
-public enum BmsState implements OptionsEnum {
- UNDEFINED(-1, "Undefined"), //
- OFF(0, "Off"), //
- INIT(2, "Init"), //
- STANDBY(4, "Standby"), //
- READY(8, "Ready"), //
- OPERATION(16, "Operation"), //
- ERROR(32, "Error"), //
- PRE_HEAT(64, "Pre-Heat"), //
- PRE_HEAT_COMPLETED(128, "Pre-Heat completed"), //
- PRE_CHARGE(256, "Precharge"), //
- PRE_CHARGE_COMPLETED(512, "Precharge completed"), //
- STATE_UNKNOWN(32768, "Unknown undefined");
-
- private final int value;
- private final String name;
-
- private BmsState(int value, String name) {
- this.value = value;
- this.name = 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.bmw/src/io/openems/edge/battery/bmw/enums/BmsStateCommand.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BmsStateCommand.java
deleted file mode 100644
index a7b3f3ff006..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/BmsStateCommand.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.openems.edge.battery.bmw.enums;
-
-import io.openems.common.types.OptionsEnum;
-
-public enum BmsStateCommand implements OptionsEnum {
- UNDEFINED(-1, "Undefined"), //
- ENABLE_RESERVED_BATTERY(0, "Enable aux power supply"), //
- WAKE_UP(1, "Wake up from stop"), //
- CLOSE_CONTACTOR(2, "Close Contactor"), //
- CLOSE_PRECHARGE(3, "Close Precharge"), //
- CLEAR_ERROR(14, "Clear BMS Error"), //
- RESET_BMS(15, "Reset BMS");
-
- private final int value;
- private final String name;
-
- private BmsStateCommand(int value, String name) {
- this.value = value;
- this.name = 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.bmw/src/io/openems/edge/battery/bmw/enums/ErrorBits1.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/ErrorBits1.java
deleted file mode 100644
index 750bdfe08ac..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/ErrorBits1.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package io.openems.edge.battery.bmw.enums;
-
-import io.openems.common.types.OptionsEnum;
-
-public enum ErrorBits1 implements OptionsEnum {
- UNDEFINED(-1, "Undefined"), //
- UNSPECIFIED(0, "Unspecified Error"), //
- LOW_VOLTAGE(1, "Low Voltage Error"), //
- HIGH_VOLTAGE(2, "High Voltage Error"), //
- CHARGE_CURRENT(3, "Charge Current Error"), //
- DISCHARGE_CURRENT(4, "Discharge Current Error"), //
- CHARGE_POWER(5, "Charge Power Error"), //
- DISCHARGE_POWER(6, "Discharge Power Error"), //
- LOW_SOC(7, "Low SoC Error"), //
- HIGH_SOC(8, "High SoC Error"), //
- LOW_TEMPERATURE(9, "Low Temperature Error"), //
- HIGH_TEMPERATURE(10, "High Temperature Error"), //
- INSULATION(11, "Insulation Error"), //
- CONTACTOR_FUSE(12, "Contactor/Fuse Error"), //
- SENSOR(13, "Sensor Error"), //
- IMBALANCE(14, "Imbalance Error"), //
- COMMUNICATION(15, "Communication Error");
-
- private final int value;
- private final String name;
-
- private ErrorBits1(int value, String name) {
- this.value = value;
- this.name = 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.bmw/src/io/openems/edge/battery/bmw/enums/ErrorBits2.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/ErrorBits2.java
deleted file mode 100644
index f4057036302..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/ErrorBits2.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package io.openems.edge.battery.bmw.enums;
-
-import io.openems.common.types.OptionsEnum;
-
-public enum ErrorBits2 implements OptionsEnum {
- UNDEFINED(-1, "Undefined"), //
- CONTAINER(0, "Container Error"), //
- SOH(1, "SoH Error"), //
- RACK_STRING(2, "Rack/String Error");
-
- private final int value;
- private final String name;
-
- private ErrorBits2(int value, String name) {
- this.value = value;
- this.name = 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.bmw/src/io/openems/edge/battery/bmw/enums/State.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/State.java
index bb3e784428f..f709ce765bd 100644
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/State.java
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/State.java
@@ -36,4 +36,4 @@ public OptionsEnum getUndefined() {
return UNDEFINED;
}
-}
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/WarningBits1.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/WarningBits1.java
deleted file mode 100644
index fd5e407ecbd..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/WarningBits1.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package io.openems.edge.battery.bmw.enums;
-
-import io.openems.common.types.OptionsEnum;
-
-public enum WarningBits1 implements OptionsEnum {
- UNDEFINED(-1, "Undefined"), //
- UNSPECIFIED(0, "Unspecified Warning"), //
- LOW_VOLTAGE(1, "Low Voltage Warning"), //
- HIGH_VOLTAGE(2, "High Voltage Warning"), //
- CHARGE_CURRENT(3, "Charge Current Warning"), //
- DISCHARGE_CURRENT(4, "Discharge Current Warning"), //
- CHARGE_POWER(5, "Charge Power Warning"), //
- DISCHARGE_POWER(6, "Discharge Power Warning"), //
- LOW_SOC(7, "Low SoC Warning"), //
- HIGH_SOC(8, "High SoC Warning"), //
- LOW_TEMPERATURE(9, "Low Temperature Warning"), //
- HIGH_TEMPERATURE(10, "High Temperature Warning"), //
- INSULATION(11, "Insulation Warning"), //
- CONTACTOR_FUSE(12, "Contactor/Fuse Warning"), //
- SENSOR(13, "Sensor Warning"), //
- IMBALANCE(14, "Imbalance Warning"), //
- COMMUNICATION(15, "Communication Warning");
-
- private final int value;
- private final String name;
-
- private WarningBits1(int value, String name) {
- this.value = value;
- this.name = 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.bmw/src/io/openems/edge/battery/bmw/enums/WarningBits2.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/WarningBits2.java
deleted file mode 100644
index 9b2a4686753..00000000000
--- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/enums/WarningBits2.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package io.openems.edge.battery.bmw.enums;
-
-import io.openems.common.types.OptionsEnum;
-
-public enum WarningBits2 implements OptionsEnum {
- UNDEFINED(-1, "Undefined"), //
- CONTAINER(0, "Container Warning"), //
- SOH(1, "SoH Warning"), //
- RACK_STRING(2, "Rack/String Warning");
-
- private final int value;
- private final String name;
-
- private WarningBits2(int value, String name) {
- this.value = value;
- this.name = 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.bmw/src/io/openems/edge/battery/bmw/statemachine/Context.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/Context.java
new file mode 100644
index 00000000000..84075427b05
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/Context.java
@@ -0,0 +1,59 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import java.time.Clock;
+import java.util.Map;
+
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
+import io.openems.edge.battery.bmw.BatteryBmwImpl;
+import io.openems.edge.bridge.http.api.BridgeHttp;
+import io.openems.edge.bridge.http.api.BridgeHttp.Endpoint;
+import io.openems.edge.bridge.http.api.HttpMethod;
+import io.openems.edge.bridge.modbus.api.BridgeModbusTcp;
+import io.openems.edge.common.statemachine.AbstractContext;
+
+public class Context extends AbstractContext {
+
+ private static final String SPLIT_REGEX = "(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)";
+
+ protected final Clock clock;
+ protected final BridgeHttp httpBridge;
+
+ public Context(BatteryBmwImpl parent, Clock clock, BridgeHttp httpBridge) {
+ super(parent);
+ this.clock = clock;
+ this.httpBridge = httpBridge;
+ }
+
+ protected void setComponent(Context context, String uri) throws OpenemsNamedException {
+ final var battery = context.getParent();
+ final var http = context.httpBridge;
+ var map = Map.of(//
+ "Authorization", "Bearer " + battery.getToken(), //
+ "Content-Type", "application/json");
+ final var id = battery.id().split(SPLIT_REGEX);
+ final var url = battery.getUrl((BridgeModbusTcp) battery.getBridgeModbus(), uri, id[1]);
+ var postData = "{data: {data: \"1\"}}";
+ final var endPoint = new Endpoint(url, HttpMethod.POST, BridgeHttp.DEFAULT_CONNECT_TIMEOUT,
+ BridgeHttp.DEFAULT_READ_TIMEOUT, postData, map);
+ http.request(endPoint);
+ }
+
+ /**
+ * Gets the endpoint.
+ *
+ * @param uri the uri state or release
+ * @param outdata write data
+ * @return {@link Endpoint}
+ */
+ public Endpoint getEndpoint(String uri, String outdata) {
+ final var battery = this.getParent();
+ var map = Map.of(//
+ "Authorization", "Bearer " + battery.getToken(), //
+ "Content-Type", "application/json");
+ final var id = battery.id().split(SPLIT_REGEX);
+ final var url = battery.getUrl((BridgeModbusTcp) battery.getBridgeModbus(), uri, id[1]);
+ final var endPoint = new Endpoint(url, HttpMethod.GET, BridgeHttp.DEFAULT_CONNECT_TIMEOUT,
+ BridgeHttp.DEFAULT_READ_TIMEOUT, outdata, map);
+ return endPoint;
+ }
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/ErrorHandler.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/ErrorHandler.java
new file mode 100644
index 00000000000..b99d4a1699a
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/ErrorHandler.java
@@ -0,0 +1,32 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.common.statemachine.StateHandler;
+
+public class ErrorHandler extends StateHandler {
+
+ @Override
+ protected void onEntry(Context context) throws OpenemsNamedException {
+ final var battery = context.getParent();
+ battery.stop();
+ }
+
+ @Override
+ public State runAndGetNextState(Context context) {
+ final var battery = context.getParent();
+ if (!battery.hasFaults() && battery.isShutdown()) {
+ return State.STOPPED;
+ }
+ return State.ERROR;
+ }
+
+ @Override
+ protected void onExit(Context context) {
+ final var battery = context.getParent();
+ battery._setUnexpectedRunningState(false);
+ battery._setUnexpectedStoppedState(false);
+ battery._setTimeoutStartBattery(false);
+ battery._setTimeoutStopBattery(false);
+ }
+}
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoRunningHandler.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoRunningHandler.java
new file mode 100644
index 00000000000..3caa207e347
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoRunningHandler.java
@@ -0,0 +1,164 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+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.timedata.Timeout;
+import io.openems.common.utils.EnumUtils;
+import io.openems.common.utils.JsonUtils;
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.bridge.http.api.BridgeHttp.Endpoint;
+import io.openems.edge.bridge.http.api.BridgeHttpCycle.CycleEndpoint;
+import io.openems.edge.bridge.http.api.HttpResponse;
+import io.openems.edge.common.statemachine.StateHandler;
+
+public class GoRunningHandler extends StateHandler {
+
+ private static final int TIMEOUT = 120; // [s]
+ private static final String URI_STATE = "bcsPowerState";
+ private static final String URI_RELEASE = "releaseStatus";
+ private final Logger log = LoggerFactory.getLogger(GoRunningHandler.class);
+
+ private boolean resultState = false;
+ private boolean resultRelease = false;
+
+ private CycleEndpoint cycleStateEndpoint;
+ private CycleEndpoint cycleReleaseEndpoint;
+
+ protected static record GoRunningState(GoRunningSubState subState) {
+ }
+
+ protected GoRunningState goRunningState;
+ private final Timeout timeout = Timeout.ofSeconds(TIMEOUT);
+
+ @Override
+ protected void onEntry(Context context) throws OpenemsNamedException {
+ this.timeout.start(context.clock);
+ this.goRunningState = new GoRunningState(GoRunningSubState.CHECK_BCS_POWER_STATE);
+ this.cycleStateEndpoint = this.subscribeHttpEndpoints(context, URI_STATE);
+ this.cycleReleaseEndpoint = this.subscribeHttpEndpoints(context, URI_RELEASE);
+ }
+
+ @Override
+ public State runAndGetNextState(Context context) throws OpenemsNamedException {
+ final var battery = context.getParent();
+ var nextSubState = this.getNextSubState(context);
+ battery._setGoRunningStateMachine(nextSubState);
+
+ if (nextSubState != this.goRunningState.subState) {
+ // Record State changes
+ this.goRunningState = new GoRunningState(nextSubState);
+ } else if (this.timeout.elapsed(context.clock)) {
+ // Handle GoRunningHandler State-timeout
+ throw new OpenemsException("Timeout [" + TIMEOUT + "s] in GoRunning-" + this.goRunningState.subState);
+ }
+
+ if (nextSubState == GoRunningSubState.ERROR) {
+ return State.ERROR;
+ }
+
+ if (nextSubState == GoRunningSubState.FINISHED) {
+ return State.RUNNING;
+ }
+
+ return State.GO_RUNNING;
+ }
+
+ private GoRunningSubState getNextSubState(Context context) throws OpenemsNamedException {
+ final var battery = context.getParent();
+ return switch (this.goRunningState.subState) {
+ case CHECK_BCS_POWER_STATE -> {
+ yield this.resultState//
+ ? GoRunningSubState.CHECK_BCS_INVERTER_RELEASE //
+ : GoRunningSubState.ACTIVATE_BCS_POWER_STATE;
+ }
+ case ACTIVATE_BCS_POWER_STATE -> {
+ context.setComponent(context, URI_STATE);
+ if (!this.resultState) {
+ yield GoRunningSubState.ACTIVATE_BCS_POWER_STATE;
+ }
+ yield GoRunningSubState.CHECK_BCS_INVERTER_RELEASE;
+ }
+ case CHECK_BCS_INVERTER_RELEASE -> {
+ yield this.resultRelease//
+ ? GoRunningSubState.START_BATTERY //
+ : GoRunningSubState.ACTIVATE_INVERTER_RELEASE;
+ }
+ case ACTIVATE_INVERTER_RELEASE -> {
+ context.setComponent(context, URI_RELEASE);
+ if (!this.resultRelease) {
+ yield GoRunningSubState.ACTIVATE_INVERTER_RELEASE;
+ }
+ yield GoRunningSubState.START_BATTERY;
+ }
+ case START_BATTERY -> {
+ battery.startBattery();
+ if (battery.isRunning()) {
+ yield GoRunningSubState.FINISHED;
+ }
+ yield GoRunningSubState.START_BATTERY;
+ }
+ case ERROR, UNDEFINED -> GoRunningSubState.ERROR;
+ case FINISHED -> GoRunningSubState.FINISHED;
+ };
+ }
+
+ private CycleEndpoint subscribeHttpEndpoints(Context context, String uri) {
+ final var endPoint = context.getEndpoint(uri, null);
+ return context.httpBridge.subscribeCycle(//
+ 1, //
+ endPoint, //
+ success -> this.getAndSetResult(success, endPoint), //
+ error -> context.logWarn(this.log, "Failed to retrieve component value from URI [ " + endPoint.url()
+ + " ] : " + error.getMessage()));
+ }
+
+ /**
+ * Get the result and set the condition for power state and inverter release
+ * state.
+ *
+ * @param success the Success
+ * @param endPoint the Endpoint
+ * @throws OpenemsNamedException on error
+ */
+ private void getAndSetResult(HttpResponse success, Endpoint endPoint) throws OpenemsNamedException {
+ var dataObject = JsonUtils.parse(success.data()).getAsJsonObject().get("data");
+ var value = 0;
+ if (dataObject != null && !dataObject.isJsonNull()) {
+ value = dataObject.getAsInt();
+ }
+ if (this.containsUrl(endPoint.url(), URI_STATE)) {
+ this.resultState = value == 1;
+ }
+ if (this.containsUrl(endPoint.url(), URI_RELEASE)) {
+ this.resultRelease = value == 1;
+ }
+ }
+
+ /**
+ * Check for url.
+ *
+ * @param url the url string
+ * @param uri the uri to compare and check
+ * @return boolean if the String available
+ */
+ public boolean containsUrl(String url, String uri) {
+ if (url == null || url.isEmpty()) {
+ return false;
+ }
+ return url.contains(uri);
+ }
+
+ @Override
+ protected String debugLog() {
+ return State.GO_RUNNING.asCamelCase() + "-" + EnumUtils.nameAsCamelCase(this.goRunningState.subState);
+ }
+
+ @Override
+ protected void onExit(Context context) throws OpenemsNamedException {
+ context.httpBridge.removeCycleEndpoint(this.cycleStateEndpoint);
+ context.httpBridge.removeCycleEndpoint(this.cycleReleaseEndpoint);
+ }
+}
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoRunningSubState.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoRunningSubState.java
new file mode 100644
index 00000000000..693c6080b42
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoRunningSubState.java
@@ -0,0 +1,37 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import io.openems.common.types.OptionsEnum;
+
+public enum GoRunningSubState implements OptionsEnum {
+ UNDEFINED(-1, "Undefined"), //
+ CHECK_BCS_POWER_STATE(0, "Check BCS"), //
+ ACTIVATE_BCS_POWER_STATE(1, "Activate BCS power state"), //
+ ACTIVATE_INVERTER_RELEASE(2, "Activate inverter release"), //
+ CHECK_BCS_INVERTER_RELEASE(3, "Check BCS after activation"), //
+ START_BATTERY(4, "Start battery"), //
+ FINISHED(5, "Finished"), //
+ ERROR(6, "Error");//
+
+ private final int value;
+ private final String name;
+
+ private GoRunningSubState(int value, String name) {
+ this.value = value;
+ this.name = 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.bmw/src/io/openems/edge/battery/bmw/statemachine/GoStoppedHandler.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoStoppedHandler.java
new file mode 100644
index 00000000000..ec9fe8fbbf5
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/GoStoppedHandler.java
@@ -0,0 +1,41 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import java.time.Duration;
+import java.time.Instant;
+
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.common.statemachine.StateHandler;
+
+public class GoStoppedHandler extends StateHandler {
+
+ private static final int TIMEOUT = 120;
+ private Instant timeAtEntry = Instant.MIN;
+
+ @Override
+ protected void onEntry(Context context) throws OpenemsNamedException {
+ this.timeAtEntry = Instant.now(context.clock);
+ }
+
+ @Override
+ public State runAndGetNextState(Context context) throws OpenemsNamedException {
+ final var battery = context.getParent();
+ if (battery.hasFaults()) {
+ return State.ERROR;
+ }
+
+ var isTimeout = Duration.between(this.timeAtEntry, //
+ Instant.now(context.clock)).getSeconds() > TIMEOUT;
+ battery._setTimeoutStopBattery(isTimeout);
+ if (isTimeout) {
+ return State.ERROR;
+ }
+
+ if (battery.isShutdown()) {
+ return State.STOPPED;
+ }
+
+ battery.stopBattery();
+ return State.GO_STOPPED;
+ }
+}
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/RunningHandler.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/RunningHandler.java
new file mode 100644
index 00000000000..e05d2f7bb67
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/RunningHandler.java
@@ -0,0 +1,24 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.common.startstop.StartStop;
+import io.openems.edge.common.statemachine.StateHandler;
+
+public class RunningHandler extends StateHandler {
+
+ @Override
+ public State runAndGetNextState(Context context) {
+ final var battery = context.getParent();
+ if (battery.hasFaults()) {
+ return State.ERROR;
+ }
+
+ if (!battery.isRunning()) {
+ battery._setUnexpectedRunningState(true);
+ return State.ERROR;
+ }
+
+ battery._setStartStop(StartStop.START);
+ return State.RUNNING;
+ }
+}
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/StateMachine.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/StateMachine.java
new file mode 100644
index 00000000000..d0e027ebc25
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/StateMachine.java
@@ -0,0 +1,70 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import io.openems.common.types.OptionsEnum;
+import io.openems.edge.common.statemachine.AbstractStateMachine;
+import io.openems.edge.common.statemachine.StateHandler;
+
+public class StateMachine extends AbstractStateMachine {
+
+ public enum State implements io.openems.edge.common.statemachine.State, OptionsEnum {
+ UNDEFINED(-1), //
+
+ GO_RUNNING(10), //
+ RUNNING(11), //
+
+ GO_STOPPED(20), //
+ STOPPED(21), //
+
+ ERROR(30), //
+ ;
+
+ private final int value;
+
+ private State(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public int getValue() {
+ return this.value;
+ }
+
+ @Override
+ public String getName() {
+ return this.name();
+ }
+
+ @Override
+ public OptionsEnum getUndefined() {
+ return UNDEFINED;
+ }
+
+ @Override
+ public State[] getStates() {
+ return State.values();
+ }
+ }
+
+ public StateMachine(State initialState) {
+ super(initialState);
+ }
+
+ @Override
+ public StateHandler getStateHandler(State state) {
+ switch (state) {
+ case UNDEFINED:
+ return new UndefinedHandler();
+ case GO_RUNNING:
+ return new GoRunningHandler();
+ case RUNNING:
+ return new RunningHandler();
+ case GO_STOPPED:
+ return new GoStoppedHandler();
+ case STOPPED:
+ return new StoppedHandler();
+ case ERROR:
+ return new ErrorHandler();
+ }
+ throw new IllegalArgumentException("Unknown State [" + state + "]");
+ }
+}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/StoppedHandler.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/StoppedHandler.java
new file mode 100644
index 00000000000..f82d36f8863
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/StoppedHandler.java
@@ -0,0 +1,24 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.common.startstop.StartStop;
+import io.openems.edge.common.statemachine.StateHandler;
+
+public class StoppedHandler extends StateHandler {
+
+ @Override
+ public State runAndGetNextState(Context context) {
+ final var battery = context.getParent();
+ if (battery.hasFaults()) {
+ return State.ERROR;
+ }
+
+ if (!battery.isShutdown()) {
+ battery._setUnexpectedStoppedState(true);
+ return State.ERROR;
+ }
+
+ battery._setStartStop(StartStop.STOP);
+ return State.STOPPED;
+ }
+}
diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/UndefinedHandler.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/UndefinedHandler.java
new file mode 100644
index 00000000000..a3b2f983e81
--- /dev/null
+++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/statemachine/UndefinedHandler.java
@@ -0,0 +1,22 @@
+package io.openems.edge.battery.bmw.statemachine;
+
+import io.openems.edge.battery.bmw.statemachine.StateMachine.State;
+import io.openems.edge.common.statemachine.StateHandler;
+
+public class UndefinedHandler extends StateHandler {
+
+ @Override
+ public State runAndGetNextState(Context context) {
+ final var battery = context.getParent();
+ return switch (battery.getStartStopTarget()) {
+ case UNDEFINED -> State.UNDEFINED;
+ case START -> {
+ if (battery.hasFaults()) {
+ yield State.ERROR;
+ }
+ yield State.GO_RUNNING;
+ }
+ case STOP -> State.GO_STOPPED;
+ };
+ }
+}
diff --git a/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/BmwBatteryImplTest.java b/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/BmwBatteryImplTest.java
index fd5aa8847db..c54180f8ff5 100644
--- a/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/BmwBatteryImplTest.java
+++ b/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/BmwBatteryImplTest.java
@@ -1,33 +1,160 @@
package io.openems.edge.battery.bmw;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicReference;
+
import org.junit.Test;
+import org.osgi.service.event.Event;
+import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
+import io.openems.common.oem.DummyOpenemsEdgeOem;
+import io.openems.common.test.TimeLeapClock;
+import io.openems.common.types.ChannelAddress;
+import io.openems.common.utils.JsonUtils;
import io.openems.edge.battery.bmw.enums.BatteryState;
+import io.openems.edge.battery.bmw.statemachine.StateMachine;
+import io.openems.edge.bridge.http.api.HttpError;
+import io.openems.edge.bridge.http.api.HttpMethod;
+import io.openems.edge.bridge.http.api.HttpResponse;
+import io.openems.edge.bridge.http.dummy.DummyBridgeHttp;
+import io.openems.edge.bridge.http.dummy.DummyBridgeHttpFactory;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
+import io.openems.edge.common.event.EdgeEventConstants;
+import io.openems.edge.common.startstop.StartStopConfig;
+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.DummyConfigurationAdmin;
public class BmwBatteryImplTest {
- private static final String BATTERY_ID = "battery0";
+ private static final String BATTERY_ID = "battery1";
private static final String MODBUS_ID = "modbus0";
+ private static final ChannelAddress STATEMACHINE = new ChannelAddress(BATTERY_ID, "StateMachine");
+ private static final ChannelAddress BATTERY_STATE = new ChannelAddress(BATTERY_ID, "BatteryState");
+
+ private static enum Operation {
+ POWER_STATE, RELEASE_STATE
+ }
@Test
- public void test() throws Exception {
- new ComponentTest(new BmwBatteryImpl()) //
+ public void startBattery() throws Exception {
+ final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var fetcher = DummyBridgeHttpFactory.dummyEndpointFetcher();
+ final var executor = DummyBridgeHttpFactory.dummyBridgeHttpExecutor(clock, true);
+ final var dataState = new AtomicReference("{data: \"0\"}");
+ final var dataRelease = new AtomicReference("{data: \"0\"}");
+
+ fetcher.addEndpointHandler(t -> {
+
+ var operation = t.url().contains("bcsPowerState") //
+ ? Operation.POWER_STATE //
+ : Operation.RELEASE_STATE;
+
+ if (t.body() == "{userCredentials: {name: \"foo\", password: \"foo_Password\"}}") {
+ return HttpResponse.ok("token");
+ }
+
+ if (t.method() == HttpMethod.GET) {
+ return switch (operation) {
+ case POWER_STATE -> HttpResponse.ok(dataState.get());
+ case RELEASE_STATE -> HttpResponse.ok(dataRelease.get());
+ };
+ }
+ if (t.method() == HttpMethod.POST) {
+ switch (operation) {
+ case POWER_STATE -> {
+ try {
+ var jsonElement = JsonUtils.parse(t.body());
+ var update = jsonElement.getAsJsonObject().get("data");
+ dataState.set(update.toString());
+ return HttpResponse.ok("");
+ } catch (OpenemsNamedException e) {
+ throw HttpError.ResponseError.notFound();
+ }
+ }
+ case RELEASE_STATE -> {
+ try {
+ var jsonElement = JsonUtils.parse(t.body());
+ var update = jsonElement.getAsJsonObject().get("data");
+ dataRelease.set(update.toString());
+ return HttpResponse.ok("");
+ } catch (OpenemsNamedException e) {
+ throw HttpError.ResponseError.notFound();
+ }
+ }
+ }
+ ;
+ }
+ throw HttpError.ResponseError.notFound();
+ });
+
+ var dummyCycleSubscriber = DummyBridgeHttpFactory.cycleSubscriber();
+
+ var test = new ComponentTest(new BatteryBmwImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("componentManager", new DummyComponentManager(clock)) //
+ .addReference("oem", new DummyOpenemsEdgeOem()) //
+ .addReference("token", new BmwToken(new DummyBridgeHttp()))//
+ .addReference("httpBridgeFactory", DummyBridgeHttpFactory.ofBridgeImpl(//
+ () -> dummyCycleSubscriber, //
+ () -> fetcher, //
+ () -> executor)) //
+ .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)//
+ .withIpAddress("127.0.0.1")) //
.activate(MyConfig.create() //
.setId(BATTERY_ID) //
.setModbusId(MODBUS_ID) //
- .setBatteryState(BatteryState.DEFAULT) //
- .setErrorDelay(0) //
- .setMaxStartAttempts(0) //
- .setMaxStartTime(0) //
- .setPendingTolerance(0) //
- .setStartUnsuccessfulDelay(0) //
- .build()) //
- ;
- }
+ .setModbusUnitId(1) //
+ .setStartStop(StartStopConfig.START) //
+ .build());//
+ test.next(new TestCase("1")//
+ .output(STATEMACHINE, StateMachine.State.UNDEFINED));
+ test.next(new TestCase("2")//
+ .onAfterProcessImage(() -> {
+ dummyCycleSubscriber.handleEvent(
+ new Event(EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, Collections.emptyMap()));
+ }));
+ test.next(new TestCase("3")//
+ .timeleap(clock, 10, ChronoUnit.SECONDS));//
+ test.next(new TestCase("4")//
+ .onAfterProcessImage(() -> {
+ dummyCycleSubscriber.handleEvent(
+ new Event(EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, Collections.emptyMap()));
+ }));
+ test.next(new TestCase("5")//
+ .output(STATEMACHINE, StateMachine.State.GO_RUNNING));
+ test.next(new TestCase("6")//
+ .onAfterProcessImage(() -> {
+ dummyCycleSubscriber.handleEvent(
+ new Event(EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, Collections.emptyMap()));
+ }));
+ test.next(new TestCase("7")//
+ .timeleap(clock, 10, ChronoUnit.SECONDS));//
+ test.next(new TestCase("8")//
+ .onAfterProcessImage(() -> {
+ dummyCycleSubscriber.handleEvent(
+ new Event(EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, Collections.emptyMap()));
+ }));
+ test.next(new TestCase("9")//
+ .input(BATTERY_STATE, BatteryState.OPERATION));
+ test.next(new TestCase("10")//
+ .onAfterProcessImage(() -> {
+ dummyCycleSubscriber.handleEvent(
+ new Event(EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, Collections.emptyMap()));
+ }));
+ test.next(new TestCase("11")//
+ .timeleap(clock, 10, ChronoUnit.SECONDS));//
+ test.next(new TestCase("12")//
+ .onAfterProcessImage(() -> {
+ dummyCycleSubscriber.handleEvent(
+ new Event(EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE, Collections.emptyMap()));
+ }));
+ test.next(new TestCase("13")//
+ .output(STATEMACHINE, StateMachine.State.RUNNING));
+ }
}
diff --git a/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/MyConfig.java b/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/MyConfig.java
index 5d23ae84e08..92c66eafcca 100644
--- a/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/MyConfig.java
+++ b/io.openems.edge.battery.bmw/test/io/openems/edge/battery/bmw/MyConfig.java
@@ -2,21 +2,16 @@
import io.openems.common.test.AbstractComponentConfig;
import io.openems.common.utils.ConfigUtils;
-import io.openems.edge.battery.bmw.enums.BatteryState;
+import io.openems.edge.common.startstop.StartStopConfig;
@SuppressWarnings("all")
public class MyConfig extends AbstractComponentConfig implements Config {
protected static class Builder {
- private String id;
- private String modbusId;
+ private String id = null;
+ private String modbusId = null;
private int modbusUnitId;
- private BatteryState batteryState;
- private long errorDelay;
- private int maxStartAttempts;
- private int maxStartTime;
- private int startUnsuccessfulDelay;
- private int pendingTolerance;
+ private StartStopConfig startStop;
private Builder() {
}
@@ -31,33 +26,13 @@ public Builder setModbusId(String modbusId) {
return this;
}
- public Builder setBatteryState(BatteryState batteryState) {
- this.batteryState = batteryState;
+ public Builder setStartStop(StartStopConfig startStop) {
+ this.startStop = startStop;
return this;
}
- public Builder setErrorDelay(long errorDelay) {
- this.errorDelay = errorDelay;
- return this;
- }
-
- public Builder setMaxStartAttempts(int maxStartAttempts) {
- this.maxStartAttempts = maxStartAttempts;
- return this;
- }
-
- public Builder setMaxStartTime(int maxStartTime) {
- this.maxStartTime = maxStartTime;
- return this;
- }
-
- public Builder setStartUnsuccessfulDelay(int startUnsuccessfulDelay) {
- this.startUnsuccessfulDelay = startUnsuccessfulDelay;
- return this;
- }
-
- public Builder setPendingTolerance(int pendingTolerance) {
- this.pendingTolerance = pendingTolerance;
+ public Builder setModbusUnitId(int modbusUnitId) {
+ this.modbusUnitId = modbusUnitId;
return this;
}
@@ -98,33 +73,7 @@ public int modbusUnitId() {
}
@Override
- public BatteryState batteryState() {
- return this.builder.batteryState;
- }
-
- @Override
- public long errorDelay() {
- return this.builder.errorDelay;
- }
-
- @Override
- public int maxStartAttempts() {
- return this.builder.maxStartAttempts;
- }
-
- @Override
- public int maxStartTime() {
- return this.builder.maxStartTime;
+ public StartStopConfig startStop() {
+ return this.builder.startStop;
}
-
- @Override
- public int startUnsuccessfulDelay() {
- return this.builder.startUnsuccessfulDelay;
- }
-
- @Override
- public int pendingTolerance() {
- return this.builder.pendingTolerance;
- }
-
}
\ No newline at end of file
diff --git a/io.openems.edge.battery.bydcommercial/.classpath b/io.openems.edge.battery.bydcommercial/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.battery.bydcommercial/.classpath
+++ b/io.openems.edge.battery.bydcommercial/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130.java
index 2dedfc9f26a..72d31daa444 100644
--- a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130.java
+++ b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130.java
@@ -787,27 +787,27 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
LEVEL1_CHARGE_CURRENT_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 2 Charge Current High Alarm Level 2")), //
- LEVEL2_CELL_VOLTAGE_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CELL_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Voltage High Alarm Level 3")), //
- LEVEL2_CELL_VOLTAGE_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_CELL_VOLTAGE_LOW(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Voltage Low Alarm Level 3")), //
- LEVEL2_CELL_VOLTAGE_DIFF_TOO_BIG(Doc.of(Level.FAULT) //
+ LEVEL2_CELL_VOLTAGE_DIFF_TOO_BIG(Doc.of(Level.WARNING) //
.text("Alarm Level 3 Battery Cells Unbalanced")), //
- LEVEL2_DISCHARGE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Discharge Temperature High Alarm Level 3")), //
- LEVEL2_DISCHARGE_TEMP_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_TEMP_LOW(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Discharge Temperature Low Alarm Level 3")), //
- LEVEL2_CHARGE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Charge Temperature High Alarm Level 3")), //
- LEVEL2_CHARGE_TEMP_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_TEMP_LOW(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Charge Temperature Low Alarm Level 3")), //
- LEVEL2_TEMP_DIFF_TOO_BIG(Doc.of(Level.FAULT) //
+ LEVEL2_TEMP_DIFF_TOO_BIG(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Temperature Diff High Alarm Level 3")), //
- LEVEL2_POWER_POLE_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_POWER_POLE_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 3 Cell Temperature High Alarm Level 3")), //
- LEVEL2_DISCHARGE_CURRENT_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_CURRENT_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 3 Discharge Current High Alarm Level 3")), //
- LEVEL2_CHARGE_CURRENT_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_CURRENT_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 3 Charge Current High Alarm Level 3")), //
ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH(Doc.of(Level.WARNING) //
@@ -822,69 +822,69 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
.text("Cluster 1 Total Voltage Low Alarm Level 1")), //
ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 1 Total Voltage High Alarm Level 1")), //
- ALARM_FUSE(Doc.of(Level.FAULT) //
+ ALARM_FUSE(Doc.of(Level.WARNING) //
.text(" Fuse Alarm")), //
SHIELDED_SWITCH_STATE(Doc.of(Level.WARNING) //
.text("Shielded switch state")), //
ALARM_BAU_COMMUNICATION(Doc.of(Level.WARNING) //
.text("BAU Communication Alarm")), //
- ALARM_INSULATION_CHECK(Doc.of(Level.FAULT) //
+ ALARM_INSULATION_CHECK(Doc.of(Level.WARNING) //
.text("Inuslation Resistance Alarm")), //
ALARM_CURRENT_SENSOR(Doc.of(Level.WARNING) //
.text("Current Sensor Alarm")), //
ALARM_BCU_BMU_COMMUNICATION(Doc.of(Level.WARNING) //
.text("BCU BMU Communication Alarm")), //
- ALARM_CONTACTOR_ADHESION(Doc.of(Level.FAULT)//
+ ALARM_CONTACTOR_ADHESION(Doc.of(Level.WARNING)//
.text("Contactor Adhesion Alarm ")), //
ALARM_BCU_NTC(Doc.of(Level.WARNING) //
.text("BCU NTC Alarm")), //
ALARM_SLAVE_CONTROL_SUMMARY(Doc.of(Level.WARNING) //
.text("Slave Control Summary Alarm")), //
- FAILURE_INITIALIZATION(Doc.of(Level.FAULT) //
+ FAILURE_INITIALIZATION(Doc.of(Level.WARNING) //
.text("Initialization failure")), //
- FAILURE_EEPROM(Doc.of(Level.FAULT) //
+ FAILURE_EEPROM(Doc.of(Level.WARNING) //
.text("EEPROM fault")), //
- FAILURE_EEPROM2(Doc.of(Level.FAULT) //
+ FAILURE_EEPROM2(Doc.of(Level.WARNING) //
.text("EEPROM2 fault")), //
- FAILURE_INTRANET_COMMUNICATION(Doc.of(Level.FAULT) //
+ FAILURE_INTRANET_COMMUNICATION(Doc.of(Level.WARNING) //
.text("Intranet communication fault")), //
- FAILURE_TEMP_SAMPLING_LINE(Doc.of(Level.FAULT) //
+ FAILURE_TEMP_SAMPLING_LINE(Doc.of(Level.WARNING) //
.text("Temperature sampling line fault")), //
- FAILURE_BALANCING_MODULE(Doc.of(Level.FAULT) //
+ FAILURE_BALANCING_MODULE(Doc.of(Level.WARNING) //
.text("Balancing module fault")), //
- FAILURE_TEMP_SENSOR(Doc.of(Level.FAULT) //
+ FAILURE_TEMP_SENSOR(Doc.of(Level.WARNING) //
.text("Temperature sensor fault")), //
- FAILURE_TEMP_SAMPLING(Doc.of(Level.FAULT) //
+ FAILURE_TEMP_SAMPLING(Doc.of(Level.WARNING) //
.text("Temperature sampling fault")), //
- FAILURE_VOLTAGE_SAMPLING(Doc.of(Level.FAULT) //
+ FAILURE_VOLTAGE_SAMPLING(Doc.of(Level.WARNING) //
.text("Voltage sampling fault")), //
- FAILURE_VOLTAGE_SAMPLING_LINE(Doc.of(Level.FAULT) //
+ FAILURE_VOLTAGE_SAMPLING_LINE(Doc.of(Level.WARNING) //
.text("Voltage sampling Line fault")), //
- FAILURE_SLAVE_UNIT_INITIALIZATION(Doc.of(Level.FAULT) //
+ FAILURE_SLAVE_UNIT_INITIALIZATION(Doc.of(Level.WARNING) //
.text("Failure Slave Unit Initialization")),
- FAILURE_CONNECTING_LINE(Doc.of(Level.FAULT) //
+ FAILURE_CONNECTING_LINE(Doc.of(Level.WARNING) //
.text("Connecting Line Failure")), //
- FAILURE_SAMPLING_CHIP(Doc.of(Level.FAULT) //
+ FAILURE_SAMPLING_CHIP(Doc.of(Level.WARNING) //
.text("Sampling Chip Failure")), //
- FAILURE_CONTACTOR(Doc.of(Level.FAULT) //
+ FAILURE_CONTACTOR(Doc.of(Level.WARNING) //
.text("Contactor Failure")), //
- FAILURE_PASSIVE_BALANCE(Doc.of(Level.FAULT) //
+ FAILURE_PASSIVE_BALANCE(Doc.of(Level.WARNING) //
.text("Passive Balance Failure")), //
- FAILURE_PASSIVE_BALANCE_TEMP(Doc.of(Level.FAULT) //
+ FAILURE_PASSIVE_BALANCE_TEMP(Doc.of(Level.WARNING) //
.text("Passive Balance Temp Failure")), //
- FAILURE_ACTIVE_BALANCE(Doc.of(Level.FAULT) //
+ FAILURE_ACTIVE_BALANCE(Doc.of(Level.WARNING) //
.text("Active Balance Failure")), //
- FAILURE_LTC6803(Doc.of(Level.FAULT) //
+ FAILURE_LTC6803(Doc.of(Level.WARNING) //
.text("LTC6803 sfault")), //
- FAILURE_CONNECTOR_WIRE(Doc.of(Level.FAULT) //
+ FAILURE_CONNECTOR_WIRE(Doc.of(Level.WARNING) //
.text("connector wire fault")), //
- FAILURE_SAMPLING_WIRE(Doc.of(Level.FAULT) //
+ FAILURE_SAMPLING_WIRE(Doc.of(Level.WARNING) //
.text("sampling wire fault")), //
- PRECHARGE_TAKING_TOO_LONG(Doc.of(Level.FAULT) //
+ PRECHARGE_TAKING_TOO_LONG(Doc.of(Level.WARNING) //
.text("precharge time was too long")), //
NEED_CHARGE(Doc.of(Level.WARNING) //
.text("Battery Need Charge")), //
- FAULT(Doc.of(Level.FAULT) //
+ FAULT(Doc.of(Level.WARNING) //
.text("battery fault state")), //
STATE_MACHINE(Doc.of(State.values()) //
.text("Current State of State-Machine")), //
@@ -928,76 +928,76 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
.text("ALARM LEVEL 2 SOH LOWER")), //
LEVEL1_PACK_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 2 PACK TEMP HIGH")), //
- LEVEL2_SYSTEM_VOLTAGE_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_SYSTEM_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 SYSTEM VOLTAGE HIGH")), //
- LEVEL2_SYSTEM_VOLTAGE_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_SYSTEM_VOLTAGE_LOW(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 SYSTEM VOLTAGE LOW")), //
- LEVEL2_SYSTEM_VOLTAGE_UNBALANCED(Doc.of(Level.FAULT) //
+ LEVEL2_SYSTEM_VOLTAGE_UNBALANCED(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 SYSTEM VOLTAGE UNBALANCED")), //
- LEVEL2_INSULATION_RESISTANCE_LOWER(Doc.of(Level.FAULT) //
+ LEVEL2_INSULATION_RESISTANCE_LOWER(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 INSULATION RESISTANCE LOWER")), //
- LEVEL2_POS_INSULATION_RESISTANCE_LOWER(Doc.of(Level.FAULT) //
+ LEVEL2_POS_INSULATION_RESISTANCE_LOWER(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 POS INSULATION RESISTANCE LOWER")), //
- LEVEL2_NEG_INSULATION_RESISTANCE_LOWER(Doc.of(Level.FAULT) //
+ LEVEL2_NEG_INSULATION_RESISTANCE_LOWER(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 NEG INSULATION RESISTANCE LOWER")), //
- LEVEL2_SYSTEM_SOC_LOWER(Doc.of(Level.FAULT) //
+ LEVEL2_SYSTEM_SOC_LOWER(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 SYSTEM SOC LOWER")), //
- LEVEL2_SYSTEM_SOC_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_SYSTEM_SOC_HIGH(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 SYSTEM SOC HIGH")), //
LEVEL2_SOH_LOWER(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 SOH LOWER")), //
- LEVEL2_PACK_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_PACK_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("ALARM LEVEL 3 PACK TEMP HIGH")), //
- SLAVE_11_COMMUNICATION_ERROR(Doc.of(Level.FAULT)//
+ SLAVE_11_COMMUNICATION_ERROR(Doc.of(Level.WARNING)//
.text("Master control and Slave control Communication Fault 1 SLAVE_CTRL_11")), //
- SLAVE_12_COMMUNICATION_ERROR(Doc.of(Level.FAULT)//
+ SLAVE_12_COMMUNICATION_ERROR(Doc.of(Level.WARNING)//
.text("Master control and Slave control Communication Fault 1 SLAVE_CTRL_12")), //
- SLAVE_13_COMMUNICATION_ERROR(Doc.of(Level.FAULT)//
+ SLAVE_13_COMMUNICATION_ERROR(Doc.of(Level.WARNING)//
.text("Master control and Slave control Communication Fault 1 SLAVE_CTRL_13")), //
- SLAVE_14_COMMUNICATION_ERROR(Doc.of(Level.FAULT)//
+ SLAVE_14_COMMUNICATION_ERROR(Doc.of(Level.WARNING)//
.text("Master control and Slave control Communication Fault 1 SLAVE_CTRL_14")), //
- SLAVE_15_COMMUNICATION_ERROR(Doc.of(Level.FAULT)//
+ SLAVE_15_COMMUNICATION_ERROR(Doc.of(Level.WARNING)//
.text("Master control and Slave control Communication Fault 1 SLAVE_CTRL_15")), //
- SLAVE_16_COMMUNICATION_ERROR(Doc.of(Level.FAULT)//
+ SLAVE_16_COMMUNICATION_ERROR(Doc.of(Level.WARNING)//
.text("Master control and Slave control Communication Fault 1 SLAVE_CTRL_16")), //
- SLAVE_17_COMMUNICATION_ERROR(Doc.of(Level.FAULT)//
+ SLAVE_17_COMMUNICATION_ERROR(Doc.of(Level.WARNING)//
.text("Master control and Slave control Communication Fault 1 SLAVE_CTRL_17")), //
- SLAVE_18_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_18_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_18")), //
- SLAVE_19_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_19_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_19")), //
- SLAVE_20_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_20_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_20")), //
- SLAVE_21_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_21_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_21")), //
- SLAVE_22_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_22_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_22")), //
- SLAVE_23_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_23_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_23")), //
- SLAVE_24_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_24_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_24")), //
- SLAVE_25_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_25_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_25")), //
- SLAVE_26_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_26_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_26")), //
- SLAVE_27_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_27_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_27")), //
- SLAVE_28_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_28_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_28")), //
- SLAVE_29_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_29_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_29")), //
- SLAVE_30_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_30_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_30")), //
- SLAVE_31_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_31_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_31")), //
- SLAVE_32_COMMUNICATION_ERROR(Doc.of(Level.FAULT) //
+ SLAVE_32_COMMUNICATION_ERROR(Doc.of(Level.WARNING) //
.text("Master control and Slave control Communication Fault 2 SLAVE_CTRL_31")), //
// OpenEMS Faults
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
- MAX_START_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_START_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of start attempts failed")), //
- MAX_STOP_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_STOP_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of stop attempts failed")), //
;
diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java
index 0dae16c2fcd..b838288b319 100644
--- a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java
+++ b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java
@@ -4,6 +4,7 @@
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce;
+import static io.openems.edge.bridge.modbus.api.ModbusUtils.FunctionCode.FC3;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -964,7 +965,7 @@ public StartStop getStartStopTarget() {
BydBatteryBoxCommercialC130Impl.this.isModbusProtocolInitialized = true;
// Try to read MODULE_QTY Register
- readElementOnce(this.getModbusProtocol(), ModbusUtils::doNotRetry, new UnsignedWordElement(0x210D))
+ readElementOnce(FC3, this.getModbusProtocol(), ModbusUtils::doNotRetry, new UnsignedWordElement(0x210D))
.thenAccept(moduleQtyValue -> {
if (moduleQtyValue != null) {
// Register is available -> add Registers for current hardware to protocol
diff --git a/io.openems.edge.battery.bydcommercial/test/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130ImplTest.java b/io.openems.edge.battery.bydcommercial/test/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130ImplTest.java
index 91d0b85e1c1..1b87571c888 100644
--- a/io.openems.edge.battery.bydcommercial/test/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130ImplTest.java
+++ b/io.openems.edge.battery.bydcommercial/test/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130ImplTest.java
@@ -1,28 +1,30 @@
package io.openems.edge.battery.bydcommercial;
+import static io.openems.edge.common.startstop.StartStopConfig.AUTO;
+
import org.junit.Test;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
-import io.openems.edge.common.startstop.StartStopConfig;
+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.DummyConfigurationAdmin;
public class BydBatteryBoxCommercialC130ImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
@Test
public void test() throws Exception {
new ComponentTest(new BydBatteryBoxCommercialC130Impl()) //
+ .addReference("componentManager", new DummyComponentManager()) //
.addReference("cm", new DummyConfigurationAdmin()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
- .setStartStop(StartStopConfig.AUTO) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
+ .setStartStop(AUTO) //
.build()) //
- ;
+ .next(new TestCase()) //
+ .deactivate();
}
}
diff --git a/io.openems.edge.battery.fenecon.commercial/.classpath b/io.openems.edge.battery.fenecon.commercial/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.battery.fenecon.commercial/.classpath
+++ b/io.openems.edge.battery.fenecon.commercial/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImplTest.java b/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImplTest.java
index 1226ccc9ffc..17038a5c183 100644
--- a/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImplTest.java
+++ b/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImplTest.java
@@ -1,17 +1,20 @@
package io.openems.edge.battery.fenecon.commercial;
-import java.time.Instant;
-import java.time.ZoneOffset;
+import static io.openems.common.test.TestUtils.createDummyClock;
+import static io.openems.edge.battery.api.Battery.ChannelId.CHARGE_MAX_CURRENT;
+import static io.openems.edge.battery.api.Battery.ChannelId.DISCHARGE_MAX_CURRENT;
+import static io.openems.edge.battery.api.Battery.ChannelId.SOC;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercial.ChannelId.BATTERY_SOC;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercial.ChannelId.RUNNING;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercial.ChannelId.STATE_MACHINE;
+import static io.openems.edge.common.startstop.StartStoppable.ChannelId.START_STOP;
+import static io.openems.edge.io.test.DummyInputOutput.ChannelId.INPUT_OUTPUT7;
import org.junit.Test;
-import io.openems.common.test.TimeLeapClock;
-import io.openems.common.types.ChannelAddress;
-import io.openems.edge.battery.api.Battery;
import io.openems.edge.battery.fenecon.commercial.statemachine.StateMachine;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
import io.openems.edge.common.startstop.StartStopConfig;
-import io.openems.edge.common.startstop.StartStoppable;
import io.openems.edge.common.test.AbstractComponentTest.TestCase;
import io.openems.edge.common.test.ComponentTest;
import io.openems.edge.common.test.DummyComponentManager;
@@ -20,42 +23,23 @@
public class BatteryFeneconCommercialImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
- private static final String IO_ID = "io0";
-
- private static final ChannelAddress STATE_MACHINE = new ChannelAddress(BATTERY_ID,
- BatteryFeneconCommercial.ChannelId.STATE_MACHINE.id());
- private static final ChannelAddress RUNNING = new ChannelAddress(BATTERY_ID,
- BatteryFeneconCommercial.ChannelId.RUNNING.id());
- private static final ChannelAddress BATTERY_RELAY = new ChannelAddress(IO_ID, "InputOutput7");
- private static final ChannelAddress START_STOP = new ChannelAddress(BATTERY_ID,
- StartStoppable.ChannelId.START_STOP.id());
- private static final ChannelAddress BATTERY_SOC = new ChannelAddress(BATTERY_ID,
- BatteryFeneconCommercial.ChannelId.BATTERY_SOC.id());
- private static final ChannelAddress BATTERY_MAX_DISCHARGE_CURRENT = new ChannelAddress(BATTERY_ID,
- Battery.ChannelId.DISCHARGE_MAX_CURRENT.id());
- private static final ChannelAddress BATTERY_MAX_CHARGE_CURRENT = new ChannelAddress(BATTERY_ID,
- Battery.ChannelId.CHARGE_MAX_CURRENT.id());
- private static final ChannelAddress SOC = new ChannelAddress(BATTERY_ID, Battery.ChannelId.SOC.id());
-
@Test
public void startBattery() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
new ComponentTest(new BatteryFeneconCommercialImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(1) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartStopRelay("io0/InputOutput7")//
.build())//
.next(new TestCase("Battery Relay false, starting") //
- .input(BATTERY_RELAY, false)//
+ .input("io0", INPUT_OUTPUT7, false)//
.input(RUNNING, false)// Switched Off
.output(STATE_MACHINE, StateMachine.State.UNDEFINED))//
.next(new TestCase() //
@@ -63,18 +47,18 @@ public void startBattery() throws Exception {
.next(new TestCase()//
.output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
.next(new TestCase()//
- .input(BATTERY_RELAY, true))//
+ .input("io0", INPUT_OUTPUT7, true))//
.next(new TestCase() //
.output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
.next(new TestCase()//
.input(RUNNING, true)//
- .input(BATTERY_RELAY, false))//
+ .input("io0", INPUT_OUTPUT7, false))//
.next(new TestCase() //
.output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
.next(new TestCase("Battery Running")//
.output(STATE_MACHINE, StateMachine.State.RUNNING))//
.next(new TestCase("Battery Running")//
- .output(BATTERY_RELAY, false)//
+ .output("io0", INPUT_OUTPUT7, false)//
.output(RUNNING, true) //
.output(STATE_MACHINE, StateMachine.State.RUNNING))//
@@ -83,21 +67,21 @@ public void startBattery() throws Exception {
@Test
public void stopBattery() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
new ComponentTest(new BatteryFeneconCommercialImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(1) //
.setStartStop(StartStopConfig.STOP) //
.setBatteryStartStopRelay("io0/InputOutput7")//
.build())//
.next(new TestCase("Battery Running")//
- .input(BATTERY_RELAY, false)//
+ .input("io0", INPUT_OUTPUT7, false)//
.input(RUNNING, true) //
.input(STATE_MACHINE, StateMachine.State.RUNNING))//
.next(new TestCase("Stopping") //
@@ -105,7 +89,7 @@ public void stopBattery() throws Exception {
.next(new TestCase()//
.output(STATE_MACHINE, StateMachine.State.GO_STOPPED))//
.next(new TestCase()//
- .input(BATTERY_RELAY, true)) //
+ .input("io0", INPUT_OUTPUT7, true)) //
.next(new TestCase()//
.output(STATE_MACHINE, StateMachine.State.STOPPED))//
@@ -114,15 +98,15 @@ public void stopBattery() throws Exception {
@Test
public void socManipulationMin() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
new ComponentTest(new BatteryFeneconCommercialImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(1) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartStopRelay("io0/InputOutput7")//
@@ -130,22 +114,22 @@ public void socManipulationMin() throws Exception {
.next(new TestCase("Soc")//
.input(RUNNING, true) //
.input(BATTERY_SOC, 10) //
- .input(BATTERY_MAX_DISCHARGE_CURRENT, 0) //
- .input(BATTERY_MAX_CHARGE_CURRENT, 10000) //
+ .input(DISCHARGE_MAX_CURRENT, 0) //
+ .input(CHARGE_MAX_CURRENT, 10000) //
.output(SOC, 10));
}
@Test
public void socManipulationMax() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
new ComponentTest(new BatteryFeneconCommercialImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(1) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartStopRelay("io0/InputOutput7")//
@@ -153,8 +137,8 @@ public void socManipulationMax() throws Exception {
.next(new TestCase("Soc")//
.input(RUNNING, true) //
.input(BATTERY_SOC, 98) //
- .input(BATTERY_MAX_DISCHARGE_CURRENT, 100000) //
- .input(BATTERY_MAX_CHARGE_CURRENT, 0) //
+ .input(DISCHARGE_MAX_CURRENT, 100000) //
+ .input(CHARGE_MAX_CURRENT, 0) //
.output(SOC, 100));
}
}
diff --git a/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/DynamicChannelsAndSerialNumbersTest.java b/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/DynamicChannelsAndSerialNumbersTest.java
index b28b1513542..a9db6249dc3 100644
--- a/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/DynamicChannelsAndSerialNumbersTest.java
+++ b/io.openems.edge.battery.fenecon.commercial/test/io/openems/edge/battery/fenecon/commercial/DynamicChannelsAndSerialNumbersTest.java
@@ -1,10 +1,14 @@
package io.openems.edge.battery.fenecon.commercial;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercial.ChannelId.MASTER_MCU_HARDWARE_VERSION;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercial.ChannelId.NUMBER_OF_CELLS_PER_MODULE;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercial.ChannelId.NUMBER_OF_MODULES_PER_TOWER;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercial.ChannelId.NUMBER_OF_TOWERS;
+import static io.openems.edge.battery.fenecon.commercial.BatteryFeneconCommercialImpl.VERSION_CONVERTER;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
-import io.openems.common.types.ChannelAddress;
import io.openems.edge.battery.api.Battery;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
import io.openems.edge.common.startstop.StartStopConfig;
@@ -16,24 +20,10 @@
public class DynamicChannelsAndSerialNumbersTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
- private static final String IO_ID = "io0";
-
private static final int TOWERS = 1;
private static final int MODULES = 10;
private static final int CELLS = 120;/* Read from register as cells*modules */
- private static final ChannelAddress NUMBER_OF_MODULES_PER_TOWER = new ChannelAddress(BATTERY_ID,
- "NumberOfModulesPerTower");
- private static final ChannelAddress NUMBER_OF_TOWERS = new ChannelAddress(BATTERY_ID, "NumberOfTowers");
- private static final ChannelAddress NUMBER_OF_CELLS_PER_MODULE = new ChannelAddress(BATTERY_ID,
- "NumberOfCellsPerModule");
- private static final ChannelAddress SUB_MASTER_HARDWARE_VERSION = new ChannelAddress(BATTERY_ID,
- "Tower0SubMasterHardwareVersion");
- private static final ChannelAddress MASTER_MCU_HARDWARE_VERSION = new ChannelAddress(BATTERY_ID,
- "MasterMcuHardwareVersion");
-
@Test
public void testSerialNum() throws Exception {
var battery = new BatteryFeneconCommercialImpl();
@@ -41,11 +31,11 @@ public void testSerialNum() throws Exception {
var componentTest = new ComponentTest(battery) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setBatteryStartStopRelay("io0/InputOutput0")//
.setStartStop(StartStopConfig.AUTO) //
@@ -56,10 +46,10 @@ public void testSerialNum() throws Exception {
.input(NUMBER_OF_TOWERS, TOWERS) //
.input(NUMBER_OF_MODULES_PER_TOWER, MODULES) //
.input(NUMBER_OF_CELLS_PER_MODULE, CELLS) //
- .input(SUB_MASTER_HARDWARE_VERSION, "109101BM60"));
+ .input("battery0", "Tower0SubMasterHardwareVersion", "109101BM60"));
checkDynamicChannels(battery, TOWERS, MODULES, CELLS / MODULES);
- assertEquals("011910MB06", BatteryFeneconCommercialImpl.VERSION_CONVERTER.elementToChannel("109101BM60"));
+ assertEquals("011910MB06", VERSION_CONVERTER.elementToChannel("109101BM60"));
componentTest.next(new TestCase());
componentTest.next(new TestCase());
@@ -72,7 +62,7 @@ public void testSerialNum() throws Exception {
.input(NUMBER_OF_CELLS_PER_MODULE, CELLS) //
.input(MASTER_MCU_HARDWARE_VERSION, "100201MS50"));
- assertEquals("012010SM05", BatteryFeneconCommercialImpl.VERSION_CONVERTER.elementToChannel("100201MS50"));
+ assertEquals("012010SM05", VERSION_CONVERTER.elementToChannel("100201MS50"));
}
/**
diff --git a/io.openems.edge.battery.fenecon.home/.classpath b/io.openems.edge.battery.fenecon.home/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.battery.fenecon.home/.classpath
+++ b/io.openems.edge.battery.fenecon.home/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHome.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHome.java
index 0ea7cc798ae..d4298dde150 100644
--- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHome.java
+++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHome.java
@@ -673,7 +673,7 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId
STATE_MACHINE(Doc.of(State.values()) //
.text("Current State of State-Machine")), //
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
LOW_MIN_VOLTAGE_WARNING(Doc.of(Level.WARNING) //
.text("Low min voltage warning "
diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java
index efa68f7d43d..3130b3ba4fc 100644
--- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java
+++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java
@@ -3,6 +3,7 @@
import static io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent.BitConverter.INVERT;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce;
+import static io.openems.edge.bridge.modbus.api.ModbusUtils.FunctionCode.FC3;
import java.time.Instant;
import java.util.List;
@@ -117,17 +118,12 @@ public BatteryFeneconHomeImpl() {
BatteryProtection.ChannelId.values(), //
BatteryFeneconHome.ChannelId.values() //
);
- this.updateHardwareType(BatteryFeneconHomeHardwareType.DEFAULT);
}
@Activate
private void activate(ComponentContext context, Config config) throws OpenemsException {
this.config = config;
-
- // Predefine BatteryProtection. Later adapted to the hardware type.
- this.batteryProtection = BatteryProtection.create(this) //
- .applyBatteryProtectionDefinition(new FeneconHomeBatteryProtection52(), this.componentManager) //
- .build();
+ this.updateHardwareType(BatteryFeneconHomeHardwareType.DEFAULT); // initialize to default
if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm,
"Modbus", config.modbus_id())) {
@@ -194,7 +190,7 @@ private void handleStateMachine() {
@Override
protected ModbusProtocol defineModbusProtocol() {
return new ModbusProtocol(this, //
- new FC3ReadRegistersTask(500, Priority.LOW, //
+ new FC3ReadRegistersTask(500, Priority.HIGH, //
m(new BitsWordElement(500, this) //
.bit(0, BatteryFeneconHome.ChannelId.RACK_PRE_ALARM_CELL_OVER_VOLTAGE) //
.bit(1, BatteryFeneconHome.ChannelId.RACK_PRE_ALARM_CELL_UNDER_VOLTAGE) //
@@ -271,10 +267,7 @@ protected ModbusProtocol defineModbusProtocol() {
.bit(6, BatteryFeneconHome.ChannelId.FAULT_POSITION_BCU_7) //
.bit(7, BatteryFeneconHome.ChannelId.FAULT_POSITION_BCU_8) //
.bit(8, BatteryFeneconHome.ChannelId.FAULT_POSITION_BCU_9) //
- .bit(9, BatteryFeneconHome.ChannelId.FAULT_POSITION_BCU_10))//
- ), //
-
- new FC3ReadRegistersTask(506, Priority.LOW, //
+ .bit(9, BatteryFeneconHome.ChannelId.FAULT_POSITION_BCU_10)), //
m(Battery.ChannelId.VOLTAGE, new UnsignedWordElement(506), SCALE_FACTOR_MINUS_1), // [V]
m(Battery.ChannelId.CURRENT, new SignedWordElement(507), SCALE_FACTOR_MINUS_1), // [A]
m(Battery.ChannelId.SOC, new UnsignedWordElement(508), SCALE_FACTOR_MINUS_1), // [%]
@@ -351,7 +344,7 @@ protected ModbusProtocol defineModbusProtocol() {
*/
private void detectHardwareType() throws OpenemsException {
// Set Battery-Protection
- readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull, new UnsignedWordElement(10019))
+ readElementOnce(FC3, this.getModbusProtocol(), ModbusUtils::retryOnNull, new UnsignedWordElement(10019))
.thenAccept(value -> {
if (value == null) {
return;
@@ -1066,7 +1059,7 @@ public BridgeModbus getModbus() {
}
@Override
- public ModbusProtocol getDefinedModbusProtocol() throws OpenemsException {
+ public ModbusProtocol getDefinedModbusProtocol() {
return this.getModbusProtocol();
}
diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryProtection64.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryProtection64.java
index bbad1e67bde..65dec26fc83 100644
--- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryProtection64.java
+++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/FeneconHomeBatteryProtection64.java
@@ -25,8 +25,8 @@ public PolyLine getChargeVoltageToPercent() {
.addPoint(Math.nextUp(3000), 1) //
.addPoint(3450, 1) //
.addPoint(3540, 0.08) //
- .addPoint(Math.nextDown(3550), 0.08) //
- .addPoint(3550, 0) //
+ .addPoint(Math.nextDown(3580), 0.08) //
+ .addPoint(3580, 0) //
.build();
}
@@ -70,7 +70,7 @@ public PolyLine getDischargeSocToPercent() {
@Override
public ForceDischarge.Params getForceDischargeParams() {
- return new ForceDischarge.Params(3600, 3540, 3450);
+ return new ForceDischarge.Params(3630, 3540, 3450);
}
@Override
diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/ModbusHelper.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/ModbusHelper.java
index b072d1ebdc4..4f95aa64a66 100644
--- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/ModbusHelper.java
+++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/ModbusHelper.java
@@ -1,24 +1,22 @@
package io.openems.edge.battery.fenecon.home;
-import io.openems.common.exceptions.OpenemsException;
import io.openems.edge.bridge.modbus.api.BridgeModbus;
import io.openems.edge.bridge.modbus.api.ModbusProtocol;
public interface ModbusHelper {
/**
- * Get modbus bridge.
+ * Get the {@link BridgeModbus}.
*
- * @return modbus bridge.
+ * @return the {@link BridgeModbus}
*/
public BridgeModbus getModbus();
/**
- * Get defined modbus protocol.
+ * Get defined {@link ModbusProtocol}.
*
- * @return modbus protocol
- * @throws OpenemsException on error
+ * @return the {@link ModbusProtocol}
*/
- public ModbusProtocol getDefinedModbusProtocol() throws OpenemsException;
+ public ModbusProtocol getDefinedModbusProtocol();
}
diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/Context.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/Context.java
index a2828143f76..6cb49490bb6 100644
--- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/Context.java
+++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/Context.java
@@ -32,8 +32,4 @@ public Context(BatteryFeneconHome parent, Clock clock, Boolean batteryStartUpRel
this.modbusCommunicationFailed = modbusCommunicationFailed;
this.retryModbusCommunication = retryModbusCommunication;
}
-
- protected void retryModbusCommunication() {
- this.getParent().retryModbusCommunication();
- }
}
\ No newline at end of file
diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoStoppedHandler.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoStoppedHandler.java
index a41a0bf4837..d539b27df30 100644
--- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoStoppedHandler.java
+++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/statemachine/GoStoppedHandler.java
@@ -12,26 +12,25 @@ public class GoStoppedHandler extends StateHandler {
private static int TIMEOUT = 2100; // [35 minutes in seconds]
private Instant timeAtEntry = Instant.MIN;
- private boolean didProtocolAdd = false;
+ private boolean isProtocolAdded = false;
@Override
- protected void onEntry(Context context) throws OpenemsNamedException {
- final var battery = context.getParent();
- final var modbus = battery.getModbus();
- modbus.removeProtocol(battery.id());
- this.didProtocolAdd = false;
+ protected void onEntry(Context context) {
+ // Remove the protocol to trigger BMS timeout
+ this.removeProtocol(context);
+
this.timeAtEntry = Instant.now(context.clock);
}
@Override
public State runAndGetNextState(Context context) throws OpenemsException {
- final var battery = context.getParent();
var now = Instant.now(context.clock);
- if (Duration.between(this.timeAtEntry, now).getSeconds() > TIMEOUT && !this.didProtocolAdd) {
- this.addAndRetryModbusProtocol(context);
+ if (Duration.between(this.timeAtEntry, now).getSeconds() > TIMEOUT && !this.isProtocolAdded) {
+ this.addProtocol(context);
return State.GO_STOPPED;
}
+ final var battery = context.getParent();
if (battery.getModbusCommunicationFailed()) {
return State.STOPPED;
}
@@ -40,12 +39,25 @@ public State runAndGetNextState(Context context) throws OpenemsException {
return State.GO_STOPPED;
}
- private void addAndRetryModbusProtocol(Context context) throws OpenemsException {
+ @Override
+ protected void onExit(Context context) throws OpenemsNamedException {
+ // Make sure to leave this GoStoppedHandler with added protocol
+ if (!this.isProtocolAdded) {
+ this.addProtocol(context);
+ }
+ }
+
+ private void addProtocol(Context context) {
final var battery = context.getParent();
final var modbus = battery.getModbus();
+ this.isProtocolAdded = true;
modbus.addProtocol(battery.id(), battery.getDefinedModbusProtocol());
- modbus.retryModbusCommunication(battery.id());
- this.didProtocolAdd = true;
}
+ private void removeProtocol(Context context) {
+ final var battery = context.getParent();
+ final var modbus = battery.getModbus();
+ this.isProtocolAdded = false;
+ modbus.removeProtocol(battery.id());
+ }
}
diff --git a/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImplTest.java b/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImplTest.java
index 432eccdcb6e..77062d93093 100644
--- a/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImplTest.java
+++ b/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImplTest.java
@@ -1,21 +1,38 @@
package io.openems.edge.battery.fenecon.home;
+import static io.openems.common.test.TestUtils.createDummyClock;
+import static io.openems.edge.battery.api.Battery.ChannelId.CHARGE_MAX_CURRENT;
+import static io.openems.edge.battery.api.Battery.ChannelId.CURRENT;
+import static io.openems.edge.battery.api.Battery.ChannelId.MAX_CELL_VOLTAGE;
+import static io.openems.edge.battery.api.Battery.ChannelId.MIN_CELL_VOLTAGE;
+import static io.openems.edge.battery.api.Battery.ChannelId.SOC;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.BMS_CONTROL;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.LOW_MIN_VOLTAGE_FAULT;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.LOW_MIN_VOLTAGE_WARNING;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.NUMBER_OF_MODULES_PER_TOWER;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.NUMBER_OF_TOWERS;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.STATE_MACHINE;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_0_BMS_SOFTWARE_VERSION;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_1_BMS_SOFTWARE_VERSION;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_2_BMS_SOFTWARE_VERSION;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_3_BMS_SOFTWARE_VERSION;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_4_BMS_SOFTWARE_VERSION;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHomeImpl.TIMEOUT;
+import static io.openems.edge.battery.protection.BatteryProtection.ChannelId.BP_CHARGE_BMS;
+import static io.openems.edge.battery.protection.BatteryProtection.ChannelId.BP_CHARGE_MAX_SOC;
+import static io.openems.edge.bridge.modbus.api.ModbusComponent.ChannelId.MODBUS_COMMUNICATION_FAILED;
+import static io.openems.edge.io.test.DummyInputOutput.ChannelId.INPUT_OUTPUT4;
+import static java.lang.Math.round;
+import static java.time.temporal.ChronoUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.temporal.ChronoUnit;
-
import org.junit.Test;
import io.openems.common.function.ThrowingRunnable;
-import io.openems.common.test.TimeLeapClock;
-import io.openems.common.types.ChannelAddress;
-import io.openems.edge.battery.api.Battery;
-import io.openems.edge.battery.fenecon.home.statemachine.StateMachine;
-import io.openems.edge.battery.protection.BatteryProtection;
-import io.openems.edge.bridge.modbus.api.ModbusComponent;
+import io.openems.edge.battery.fenecon.home.statemachine.StateMachine.State;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
import io.openems.edge.common.startstop.StartStopConfig;
import io.openems.edge.common.test.AbstractComponentTest.TestCase;
@@ -26,51 +43,6 @@
public class BatteryFeneconHomeImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
- private static final String IO_ID = "io0";
-
- private static final ChannelAddress STATE_MACHINE = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.STATE_MACHINE.id());
- private static final ChannelAddress LOW_MIN_VOLTAGE_WARNING = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.LOW_MIN_VOLTAGE_WARNING.id());
- private static final ChannelAddress LOW_MIN_VOLTAGE_FAULT = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.LOW_MIN_VOLTAGE_FAULT.id());
- private static final ChannelAddress LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED.id());
- private static final ChannelAddress MODBUS_COMMUNICATION_FAILED = new ChannelAddress(BATTERY_ID,
- ModbusComponent.ChannelId.MODBUS_COMMUNICATION_FAILED.id());
- private static final ChannelAddress BMS_CONTROL = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.BMS_CONTROL.id());
- private static final ChannelAddress BP_CHARGE_BMS = new ChannelAddress(BATTERY_ID,
- BatteryProtection.ChannelId.BP_CHARGE_BMS.id());
- private static final ChannelAddress MAX_CELL_VOLTAGE = new ChannelAddress(BATTERY_ID,
- Battery.ChannelId.MAX_CELL_VOLTAGE.id());
- private static final ChannelAddress CHARGE_MAX_CURRENT = new ChannelAddress(BATTERY_ID,
- Battery.ChannelId.CHARGE_MAX_CURRENT.id());
- private static final ChannelAddress CURRENT = new ChannelAddress(BATTERY_ID, Battery.ChannelId.CURRENT.id());
- private static final ChannelAddress MIN_CELL_VOLTAGE = new ChannelAddress(BATTERY_ID,
- Battery.ChannelId.MIN_CELL_VOLTAGE.id());
- private static final ChannelAddress TOWER_0_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.TOWER_0_BMS_SOFTWARE_VERSION.id());
- private static final ChannelAddress TOWER_1_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.TOWER_1_BMS_SOFTWARE_VERSION.id());
- private static final ChannelAddress TOWER_2_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.TOWER_2_BMS_SOFTWARE_VERSION.id());
- private static final ChannelAddress TOWER_3_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.TOWER_3_BMS_SOFTWARE_VERSION.id());
- private static final ChannelAddress TOWER_4_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.TOWER_4_BMS_SOFTWARE_VERSION.id());
- private static final ChannelAddress NUMBER_OF_TOWERS = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.NUMBER_OF_TOWERS.id());
- private static final ChannelAddress NUMBER_OF_MODULES_PER_TOWER = new ChannelAddress(BATTERY_ID,
- BatteryFeneconHome.ChannelId.NUMBER_OF_MODULES_PER_TOWER.id());
- private static final ChannelAddress BP_CHARGE_MAX_SOC = new ChannelAddress(BATTERY_ID,
- BatteryProtection.ChannelId.BP_CHARGE_MAX_SOC.id());
- private static final ChannelAddress SOC = new ChannelAddress(BATTERY_ID, Battery.ChannelId.SOC.id());
-
- private static final ChannelAddress BATTERY_RELAY = new ChannelAddress(IO_ID, "InputOutput4");
-
private static ThrowingRunnable assertLog(BatteryFeneconHomeImpl sut, String message) {
return () -> assertEquals(message, sut.stateMachine.debugLog());
}
@@ -82,41 +54,41 @@ private static ThrowingRunnable assertLog(BatteryFeneconHomeImpl sut,
*/
@Test
public void test() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
var sut = new BatteryFeneconHomeImpl();
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartUpRelay("io0/InputOutput4")//
.build())//
.next(new TestCase() //
.inputForce(MODBUS_COMMUNICATION_FAILED, true) //
- .input(BATTERY_RELAY, false) // Switch OFF
+ .input("io0", INPUT_OUTPUT4, false) // Switch OFF
.input(BMS_CONTROL, false) // Switched OFF
.onBeforeProcessImage(assertLog(sut, "Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED)) //
+ .output(STATE_MACHINE, State.UNDEFINED)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase()//
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOn"))) //
.next(new TestCase()//
- .input(BATTERY_RELAY, true) // Switch ON
+ .input("io0", INPUT_OUTPUT4, true) // Switch ON
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOn"))) //
.next(new TestCase()//
- .input(BATTERY_RELAY, true) // Switch ON
+ .input("io0", INPUT_OUTPUT4, true) // Switch ON
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayHold"))
- .onAfterProcessImage(() -> clock.leap(11, ChronoUnit.SECONDS))) //
+ .onAfterProcessImage(() -> clock.leap(11, SECONDS))) //
.next(new TestCase() //
- .input(BATTERY_RELAY, false) // Switch OFF
+ .input("io0", INPUT_OUTPUT4, false) // Switch OFF
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff"))) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-RetryModbusCommunication"))) //
@@ -129,17 +101,17 @@ public void test() throws Exception {
.next(new TestCase()//
.onBeforeProcessImage(assertLog(sut, "Running")) //
- .output(STATE_MACHINE, StateMachine.State.RUNNING)) //
+ .output(STATE_MACHINE, State.RUNNING)) //
// Ramp-Up ChargeMaxCurrent (0.1 A / Second)
.next(new TestCase() //
.input(BP_CHARGE_BMS, 40) //
.input(MAX_CELL_VOLTAGE, 3000)) //
.next(new TestCase("Ramp up") //
- .timeleap(clock, 100, ChronoUnit.SECONDS) //
+ .timeleap(clock, 100, SECONDS) //
.output(CHARGE_MAX_CURRENT, 10)) //
.next(new TestCase() //
- .timeleap(clock, 300, ChronoUnit.SECONDS) //
+ .timeleap(clock, 300, SECONDS) //
.output(CHARGE_MAX_CURRENT, 40))
// Full Battery
@@ -151,7 +123,7 @@ public void test() throws Exception {
.next(new TestCase() //
.input(BP_CHARGE_BMS, 40)) //
.next(new TestCase() //
- .timeleap(clock, 100, ChronoUnit.SECONDS) //
+ .timeleap(clock, 100, SECONDS) //
.output(CHARGE_MAX_CURRENT, 25)) //
;
}
@@ -163,54 +135,54 @@ public void test() throws Exception {
*/
@Test
public void test2() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
var sut = new BatteryFeneconHomeImpl();
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartUpRelay("io0/InputOutput4")//
.build())//
.next(new TestCase()//
- .input(BATTERY_RELAY, true) //
+ .input("io0", INPUT_OUTPUT4, true) //
.input(BMS_CONTROL, false) // Switched Off
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED))//
+ .output(STATE_MACHINE, State.UNDEFINED))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOn")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase()//
- .input(BATTERY_RELAY, true) // Switch ON
+ .input("io0", INPUT_OUTPUT4, true) // Switch ON
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayHold"))
- .onAfterProcessImage(() -> clock.leap(11, ChronoUnit.SECONDS))) //
+ .onAfterProcessImage(() -> clock.leap(11, SECONDS))) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
.input(BMS_CONTROL, true) // Switched On
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .input(BATTERY_RELAY, false) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .input("io0", INPUT_OUTPUT4, false) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-RetryModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForBmsControl")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
- .output(STATE_MACHINE, StateMachine.State.RUNNING));
+ .output(STATE_MACHINE, State.RUNNING));
}
/**
@@ -225,37 +197,37 @@ public void test3() throws Exception {
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartUpRelay("io0/InputOutput4")//
.build()) //
.next(new TestCase() //
- .input(BATTERY_RELAY, false) //
+ .input("io0", INPUT_OUTPUT4, false) //
.input(BMS_CONTROL, true) // Switched On
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED))//
+ .output(STATE_MACHINE, State.UNDEFINED))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-RetryModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForBmsControl")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
- .output(STATE_MACHINE, StateMachine.State.RUNNING));
+ .output(STATE_MACHINE, State.RUNNING));
}
/**
@@ -265,53 +237,53 @@ public void test3() throws Exception {
*/
@Test
public void test4() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
var sut = new BatteryFeneconHomeImpl();
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartUpRelay("io0/InputOutput4") //
.build()) //
.next(new TestCase() //
- .input(BATTERY_RELAY, false) //
+ .input("io0", INPUT_OUTPUT4, false) //
.input(BMS_CONTROL, false) // Switched Off
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED)) //
+ .output(STATE_MACHINE, State.UNDEFINED)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOn")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOn")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
// Ex; after long time if hard switch turned on....
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOn")) //
.input(BMS_CONTROL, true) // Switched On
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOn")) //
- .input(BATTERY_RELAY, true) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .input("io0", INPUT_OUTPUT4, true) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase()//
- .input(BATTERY_RELAY, true) // Switch ON
+ .input("io0", INPUT_OUTPUT4, true) // Switch ON
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayHold"))
- .onAfterProcessImage(() -> clock.leap(11, ChronoUnit.SECONDS))) //
+ .onAfterProcessImage(() -> clock.leap(11, SECONDS))) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)); //
+ .output(STATE_MACHINE, State.GO_RUNNING)); //
}
@Test
@@ -334,224 +306,221 @@ public void testGetHardwareTypeFromRegisterValue() {
@Test
public void testMinVoltageGoStopped() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
var sut = new BatteryFeneconHomeImpl();
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartUpRelay("io0/InputOutput4")//
.build())//
.next(new TestCase() //
- .input(BATTERY_RELAY, false) //
+ .input("io0", INPUT_OUTPUT4, false) //
.input(BMS_CONTROL, true) // Switched On
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED))//
+ .output(STATE_MACHINE, State.UNDEFINED))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-RetryModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForBmsControl")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
- .output(STATE_MACHINE, StateMachine.State.RUNNING))
+ .output(STATE_MACHINE, State.RUNNING))
/*
* Critical min voltage
*/
.next(new TestCase("MinCellVoltage below critical value") //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.input(CURRENT, 0) //
.output(LOW_MIN_VOLTAGE_WARNING, true) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
- .output(STATE_MACHINE, StateMachine.State.RUNNING) //
+ .output(STATE_MACHINE, State.RUNNING) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false) //
- .onAfterControllersCallbacks(
- () -> clock.leap(BatteryFeneconHomeImpl.TIMEOUT - 10, ChronoUnit.SECONDS))) //
+ .onAfterControllersCallbacks(() -> clock.leap(TIMEOUT - 10, SECONDS))) //
.next(new TestCase("MinCellVoltage below critical value - charging resets time") //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.input(CURRENT, -300) //
.output(LOW_MIN_VOLTAGE_WARNING, true) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false)) //
.next(new TestCase("MinCellVoltage below critical value - timer starts again") //
.input(CURRENT, 0) //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.output(LOW_MIN_VOLTAGE_WARNING, true) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false) //
- .onAfterControllersCallbacks(
- () -> clock.leap(BatteryFeneconHomeImpl.TIMEOUT - 10, ChronoUnit.SECONDS))) //
+ .onAfterControllersCallbacks(() -> clock.leap(TIMEOUT - 10, SECONDS))) //
.next(new TestCase("MinCellVoltage below critical value - time not passed") //
.input(CURRENT, 0) //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.output(LOW_MIN_VOLTAGE_WARNING, true) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false) //
- .onAfterControllersCallbacks(() -> clock.leap(15, ChronoUnit.SECONDS))) //
+ .onAfterControllersCallbacks(() -> clock.leap(15, SECONDS))) //
.next(new TestCase("MinCellVoltage below critical value - time passed") //
.input(CURRENT, 0) //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.output(LOW_MIN_VOLTAGE_FAULT, true) //
.output(LOW_MIN_VOLTAGE_WARNING, false) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false) //
- .output(STATE_MACHINE, StateMachine.State.RUNNING)) //
+ .output(STATE_MACHINE, State.RUNNING)) //
.next(new TestCase("MinCellVoltage below critical value - error") //
.input(LOW_MIN_VOLTAGE_FAULT, true) //
.input(CURRENT, 0) //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.output(LOW_MIN_VOLTAGE_FAULT, true) //
.output(LOW_MIN_VOLTAGE_WARNING, false) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false)) //
.next(new TestCase() //
- .output(STATE_MACHINE, StateMachine.State.ERROR)) //
+ .output(STATE_MACHINE, State.ERROR)) //
.next(new TestCase("MinCellVoltage below critical value - go stopped") //
.input(LOW_MIN_VOLTAGE_FAULT, true) //
.input(CURRENT, 0) //
// MinCellVoltage would be null, but there is not DummyTimedata for not to test
// "getPastValues"
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.output(LOW_MIN_VOLTAGE_FAULT, true) //
.output(LOW_MIN_VOLTAGE_WARNING, false) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false) //
- .output(STATE_MACHINE, StateMachine.State.GO_STOPPED) //
- .onAfterControllersCallbacks(() -> clock.leap(2_100, ChronoUnit.SECONDS))) // 35 minutes
+ .output(STATE_MACHINE, State.GO_STOPPED) //
+ .onAfterControllersCallbacks(() -> clock.leap(2_100, SECONDS))) // 35 minutes
.next(new TestCase() //
.input(MODBUS_COMMUNICATION_FAILED, true) //
) //
.next(new TestCase("MinCellVoltage below critical value - stopped") //
.input(CURRENT, 0) //
.input(MODBUS_COMMUNICATION_FAILED, true) //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.output(LOW_MIN_VOLTAGE_WARNING, false) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, true) //
- .output(STATE_MACHINE, StateMachine.State.STOPPED) //
+ .output(STATE_MACHINE, State.STOPPED) //
);
}
@Test
public void testMinVoltageCharging() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
var sut = new BatteryFeneconHomeImpl();
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartUpRelay("io0/InputOutput4")//
.build())//
.next(new TestCase() //
- .input(BATTERY_RELAY, false) //
+ .input("io0", INPUT_OUTPUT4, false) //
.input(BMS_CONTROL, true) // Switched On
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED))//
+ .output(STATE_MACHINE, State.UNDEFINED))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-RetryModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForBmsControl")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
- .output(STATE_MACHINE, StateMachine.State.RUNNING))
+ .output(STATE_MACHINE, State.RUNNING))
/*
* Critical min voltage
*/
.next(new TestCase("MinCellVoltage below critical value") //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.input(CURRENT, 0) //
.output(LOW_MIN_VOLTAGE_WARNING, true) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
- .output(STATE_MACHINE, StateMachine.State.RUNNING) //
+ .output(STATE_MACHINE, State.RUNNING) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false) //
- .onAfterControllersCallbacks(
- () -> clock.leap(BatteryFeneconHomeImpl.TIMEOUT - 10, ChronoUnit.SECONDS))) //
+ .onAfterControllersCallbacks(() -> clock.leap(TIMEOUT - 10, SECONDS))) //
.next(new TestCase("MinCellVoltage below critical value - charging resets time") //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE - 100)) //
.input(CURRENT, -300) //
.output(LOW_MIN_VOLTAGE_WARNING, true) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
- .output(STATE_MACHINE, StateMachine.State.RUNNING) //
+ .output(STATE_MACHINE, State.RUNNING) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false)) //
.next(new TestCase("MinCellVoltage below critical value - charging") //
.input(CURRENT, -2000) //
- .input(MIN_CELL_VOLTAGE, (BatteryFeneconHomeImpl.DEFAULT_CRITICAL_MIN_VOLTAGE + 50)) //
+ .input(MIN_CELL_VOLTAGE, (DEFAULT_CRITICAL_MIN_VOLTAGE + 50)) //
.output(LOW_MIN_VOLTAGE_WARNING, false) //
.output(LOW_MIN_VOLTAGE_FAULT, false) //
- .output(STATE_MACHINE, StateMachine.State.RUNNING) //
+ .output(STATE_MACHINE, State.RUNNING) //
.output(LOW_MIN_VOLTAGE_FAULT_BATTERY_STOPPED, false) //
);
}
@Test
public void testNumberOfTowers() throws Exception {
- final var clock = new TimeLeapClock(Instant.parse("2020-01-01T01:00:00.00Z"), ZoneOffset.UTC);
+ final var clock = createDummyClock();
var sut = new BatteryFeneconHomeImpl();
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
- .setBatteryStartUpRelay("io0/InputOutput4")//
- .build())//
+ .setBatteryStartUpRelay("io0/InputOutput4") //
+ .build()) //
.next(new TestCase() //
- .input(BATTERY_RELAY, false) //
+ .input("io0", INPUT_OUTPUT4, false) //
.input(BMS_CONTROL, true) // Switched On
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED))//
+ .output(STATE_MACHINE, State.UNDEFINED))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-RetryModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForBmsControl")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
- .output(STATE_MACHINE, StateMachine.State.RUNNING))
+ .output(STATE_MACHINE, State.RUNNING))
.next(new TestCase() //
.output(NUMBER_OF_TOWERS, null))
.next(new TestCase() //
@@ -610,37 +579,37 @@ public void testBatteryProtectionSocLimitations() throws Exception {
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
- .addComponent(new DummyInputOutput(IO_ID))//
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .addComponent(new DummyInputOutput("io0"))//
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.START) //
.setBatteryStartUpRelay("io0/InputOutput4")//
.build()) //
.next(new TestCase() //
- .input(BATTERY_RELAY, false) //
+ .input("io0", INPUT_OUTPUT4, false) //
.input(BMS_CONTROL, true) // Switched On
- .output(STATE_MACHINE, StateMachine.State.UNDEFINED))//
+ .output(STATE_MACHINE, State.UNDEFINED))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-Undefined")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-StartUpRelayOff")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-RetryModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING))//
+ .output(STATE_MACHINE, State.GO_RUNNING))//
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForBmsControl")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
.onBeforeProcessImage(assertLog(sut, "GoRunning-WaitForModbusCommunication")) //
- .output(STATE_MACHINE, StateMachine.State.GO_RUNNING)) //
+ .output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
- .output(STATE_MACHINE, StateMachine.State.RUNNING)) //
+ .output(STATE_MACHINE, State.RUNNING)) //
.next(new TestCase() //
.output(BP_CHARGE_MAX_SOC, 40)) //
@@ -648,23 +617,45 @@ public void testBatteryProtectionSocLimitations() throws Exception {
.input(SOC, 97) //
.output(SOC, 97)) //
.next(new TestCase() //
- .output(BP_CHARGE_MAX_SOC, (int) Math.round(40 * 0.625))) //
+ .output(BP_CHARGE_MAX_SOC, round(40 * 0.625F))) //
.next(new TestCase() //
.input(SOC, 98)) //
.next(new TestCase() //
- .output(BP_CHARGE_MAX_SOC, (int) Math.round(40 * 0.4))) //
+ .output(BP_CHARGE_MAX_SOC, round(40 * 0.4F))) //
.next(new TestCase() //
.input(SOC, 99)) //
.next(new TestCase() //
- .output(BP_CHARGE_MAX_SOC, (int) Math.round(40 * 0.2))) //
+ .output(BP_CHARGE_MAX_SOC, round(40 * 0.2F))) //
.next(new TestCase() //
.input(SOC, 100)) //
.next(new TestCase() //
- .output(BP_CHARGE_MAX_SOC, (int) Math.round(40 * 0.05))) //
+ .output(BP_CHARGE_MAX_SOC, round(40 * 0.05F))) //
.next(new TestCase() //
.input(SOC, 99)) //
.next(new TestCase() //
- .output(BP_CHARGE_MAX_SOC, (int) Math.round(40 * 0.2)) //
+ .output(BP_CHARGE_MAX_SOC, round(40 * 0.2F)) //
);
}
+
+ @Test
+ public void testReadModbus() throws Exception {
+ var sut = new BatteryFeneconHomeImpl();
+ new ComponentTest(sut) //
+ .addReference("cm", new DummyConfigurationAdmin()) //
+ .addReference("componentManager", new DummyComponentManager()) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0") //
+ .withRegister(18000, (byte) 0x00, (byte) 0x00)) // TOWER_4_BMS_SOFTWARE_VERSION
+ .activate(MyConfig.create() //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
+ .setModbusUnitId(0) //
+ .setStartStop(StartStopConfig.START) //
+ .setBatteryStartUpRelay("io0/InputOutput4")//
+ .build()) //
+
+ .next(new TestCase() //
+ .output(BatteryFeneconHome.ChannelId.TOWER_4_BMS_SOFTWARE_VERSION, 0)) //
+
+ .deactivate();
+ }
}
diff --git a/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/TowersAndModulesTest.java b/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/TowersAndModulesTest.java
index ff8b7353731..9fff171c345 100644
--- a/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/TowersAndModulesTest.java
+++ b/io.openems.edge.battery.fenecon.home/test/io/openems/edge/battery/fenecon/home/TowersAndModulesTest.java
@@ -1,11 +1,15 @@
package io.openems.edge.battery.fenecon.home;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.BATTERY_HARDWARE_TYPE;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.NUMBER_OF_MODULES_PER_TOWER;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_0_BMS_SOFTWARE_VERSION;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_1_BMS_SOFTWARE_VERSION;
+import static io.openems.edge.battery.fenecon.home.BatteryFeneconHome.ChannelId.TOWER_2_BMS_SOFTWARE_VERSION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
-import io.openems.common.types.ChannelAddress;
import io.openems.edge.battery.api.Battery;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
import io.openems.edge.common.channel.ChannelId;
@@ -17,19 +21,6 @@
public class TowersAndModulesTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
- private static final ChannelAddress NUMBER_OF_MODULES_PER_TOWER = new ChannelAddress(BATTERY_ID,
- "NumberOfModulesPerTower");
- private static final ChannelAddress TOWER_0_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- "Tower0BmsSoftwareVersion");
- private static final ChannelAddress TOWER_1_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- "Tower1BmsSoftwareVersion");
- private static final ChannelAddress TOWER_2_BMS_SOFTWARE_VERSION = new ChannelAddress(BATTERY_ID,
- "Tower2BmsSoftwareVersion");
- private static final ChannelAddress BATTERY_HARDWARE_TYPE = new ChannelAddress(BATTERY_ID, "BatteryHardwareType");
-
private static final int TOWERS = 1;
private static final int MODULES = 5;
private static final int CELLS = 14;
@@ -40,10 +31,10 @@ public void testChannelsCreatedDynamically() throws Exception {
var componentTest = new ComponentTest(battery) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setBatteryStartUpRelay("io0/Relay4") //
.setStartStop(StartStopConfig.AUTO) //
diff --git a/io.openems.edge.battery.soltaro/.classpath b/io.openems.edge.battery.soltaro/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.battery.soltaro/.classpath
+++ b/io.openems.edge.battery.soltaro/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionB.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionB.java
index 1276244de00..867c5b99a9f 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionB.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionB.java
@@ -52,80 +52,80 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
.accessMode(AccessMode.READ_WRITE)), //
// StateChannels
- MASTER_ALARM_COMMUNICATION_ERROR_WITH_SUBMASTER(Doc.of(Level.FAULT) //
+ MASTER_ALARM_COMMUNICATION_ERROR_WITH_SUBMASTER(Doc.of(Level.WARNING) //
.text("Communication error with submaster")),
- MASTER_ALARM_PCS_EMS_COMMUNICATION_FAILURE(Doc.of(Level.FAULT) //
+ MASTER_ALARM_PCS_EMS_COMMUNICATION_FAILURE(Doc.of(Level.WARNING) //
.text("PCS/EMS communication failure alarm")),
- MASTER_ALARM_PCS_EMS_CONTROL_FAIL(Doc.of(Level.FAULT) //
+ MASTER_ALARM_PCS_EMS_CONTROL_FAIL(Doc.of(Level.WARNING) //
.text("PCS/EMS control fail alarm")),
MASTER_ALARM_LEVEL_1_INSULATION(Doc.of(Level.WARNING) //
.text("System insulation alarm level 1")),
- MASTER_ALARM_LEVEL_2_INSULATION(Doc.of(Level.FAULT) //
+ MASTER_ALARM_LEVEL_2_INSULATION(Doc.of(Level.WARNING) //
.text("System insulation alarm level 2")),
- RACK_1_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_1_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 1 Level 2 Alarm")),
- RACK_1_PCS_CONTROL_FAULT(Doc.of(Level.FAULT) //
+ RACK_1_PCS_CONTROL_FAULT(Doc.of(Level.WARNING) //
.text("Rack 1 PCS control fault")),
- RACK_1_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.FAULT) //
+ RACK_1_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.WARNING) //
.text("Rack 1 Communication with master error")),
- RACK_1_DEVICE_ERROR(Doc.of(Level.FAULT) //
+ RACK_1_DEVICE_ERROR(Doc.of(Level.WARNING) //
.text("Rack 1 Device error")),
- RACK_1_CYCLE_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_1_CYCLE_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 1 Cycle over current")),
- RACK_1_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_1_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 1 Voltage difference")),
- RACK_2_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_2_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 2 Level 2 Alarm")),
- RACK_2_PCS_CONTROL_FAULT(Doc.of(Level.FAULT) //
+ RACK_2_PCS_CONTROL_FAULT(Doc.of(Level.WARNING) //
.text("Rack 2 PCS control fault")),
- RACK_2_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.FAULT) //
+ RACK_2_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.WARNING) //
.text("Rack 2 Communication with master error")),
- RACK_2_DEVICE_ERROR(Doc.of(Level.FAULT) //
+ RACK_2_DEVICE_ERROR(Doc.of(Level.WARNING) //
.text("Rack 2 Device error")),
- RACK_2_CYCLE_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_2_CYCLE_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 1 Cycle over current")),
- RACK_2_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_2_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 1 Voltage difference")),
- RACK_3_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_3_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 3 Level 2 Alarm")),
- RACK_3_PCS_CONTROL_FAULT(Doc.of(Level.FAULT) //
+ RACK_3_PCS_CONTROL_FAULT(Doc.of(Level.WARNING) //
.text("Rack 3 PCS control fault")),
- RACK_3_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.FAULT) //
+ RACK_3_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.WARNING) //
.text("Rack 3 Communication with master error")),
- RACK_3_DEVICE_ERROR(Doc.of(Level.FAULT) //
+ RACK_3_DEVICE_ERROR(Doc.of(Level.WARNING) //
.text("Rack 3 Device error")),
- RACK_3_CYCLE_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_3_CYCLE_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 1 Cycle over current")),
- RACK_3_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_3_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 1 Voltage difference")),
- RACK_4_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_4_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 4 Level 2 Alarm")),
- RACK_4_PCS_CONTROL_FAULT(Doc.of(Level.FAULT) //
+ RACK_4_PCS_CONTROL_FAULT(Doc.of(Level.WARNING) //
.text("Rack 4 PCS control fault")),
- RACK_4_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.FAULT) //
+ RACK_4_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.WARNING) //
.text("Rack 4 Communication with master error")),
- RACK_4_DEVICE_ERROR(Doc.of(Level.FAULT) //
+ RACK_4_DEVICE_ERROR(Doc.of(Level.WARNING) //
.text("Rack 4 Device error")),
- RACK_4_CYCLE_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_4_CYCLE_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 1 Cycle over current")),
- RACK_4_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_4_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 1 Voltage difference")),
- RACK_5_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_5_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 5 Level 2 Alarm")),
- RACK_5_PCS_CONTROL_FAULT(Doc.of(Level.FAULT) //
+ RACK_5_PCS_CONTROL_FAULT(Doc.of(Level.WARNING) //
.text("Rack 5 PCS control fault")),
- RACK_5_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.FAULT) //
+ RACK_5_COMMUNICATION_WITH_MASTER_ERROR(Doc.of(Level.WARNING) //
.text("Rack 5 Communication with master error")),
- RACK_5_DEVICE_ERROR(Doc.of(Level.FAULT) //
+ RACK_5_DEVICE_ERROR(Doc.of(Level.WARNING) //
.text("Rack 5 Device error")),
- RACK_5_CYCLE_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_5_CYCLE_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 1 Cycle over current")),
- RACK_5_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_5_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 1 Voltage difference")),;
private final Doc doc;
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/SingleRack.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/SingleRack.java
index 45d65c88cfb..e5d4529f972 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/SingleRack.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/SingleRack.java
@@ -1,5 +1,14 @@
package io.openems.edge.battery.soltaro.cluster.versionb;
+import static io.openems.common.channel.AccessMode.READ_WRITE;
+import static io.openems.common.channel.Level.OK;
+import static io.openems.common.channel.Level.WARNING;
+import static io.openems.common.channel.Unit.DEZIDEGREE_CELSIUS;
+import static io.openems.common.channel.Unit.MILLIAMPERE;
+import static io.openems.common.channel.Unit.MILLIVOLT;
+import static io.openems.common.channel.Unit.NONE;
+import static io.openems.common.channel.Unit.PERCENT;
+import static io.openems.common.types.OpenemsType.INTEGER;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
@@ -9,10 +18,6 @@
import java.util.Map;
import java.util.Optional;
-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.battery.soltaro.common.enums.ChargeIndication;
import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
import io.openems.edge.bridge.modbus.api.element.BitsWordElement;
@@ -426,104 +431,102 @@ private Map> createChannelMap() {
private Map createChannelIdMap() {
Map map = new HashMap<>();
- this.addEntry(map, KEY_VOLTAGE, new IntegerDoc().unit(Unit.MILLIVOLT));
- this.addEntry(map, KEY_CURRENT, new IntegerDoc().unit(Unit.MILLIAMPERE));
+ this.addEntry(map, KEY_VOLTAGE, new IntegerDoc().unit(MILLIVOLT));
+ this.addEntry(map, KEY_CURRENT, new IntegerDoc().unit(MILLIAMPERE));
this.addEntry(map, KEY_CHARGE_INDICATION, Doc.of(ChargeIndication.values()));
- this.addEntry(map, KEY_SOC, new IntegerDoc().unit(Unit.PERCENT));
- this.addEntry(map, KEY_SOH, new IntegerDoc().unit(Unit.PERCENT));
- this.addEntry(map, KEY_MAX_CELL_VOLTAGE_ID, new IntegerDoc().unit(Unit.NONE));
- this.addEntry(map, KEY_MAX_CELL_VOLTAGE, new IntegerDoc().unit(Unit.MILLIVOLT));
- this.addEntry(map, KEY_MIN_CELL_VOLTAGE_ID, new IntegerDoc().unit(Unit.NONE));
- this.addEntry(map, KEY_MIN_CELL_VOLTAGE, new IntegerDoc().unit(Unit.MILLIVOLT));
- this.addEntry(map, KEY_MAX_CELL_TEMPERATURE_ID, new IntegerDoc().unit(Unit.NONE));
- this.addEntry(map, KEY_MAX_CELL_TEMPERATURE, new IntegerDoc().unit(Unit.DEZIDEGREE_CELSIUS));
- this.addEntry(map, KEY_MIN_CELL_TEMPERATURE_ID, new IntegerDoc().unit(Unit.NONE));
- this.addEntry(map, KEY_MIN_CELL_TEMPERATURE, new IntegerDoc().unit(Unit.DEZIDEGREE_CELSIUS));
- this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW, Doc.of(Level.FAULT)
+ this.addEntry(map, KEY_SOC, new IntegerDoc().unit(PERCENT));
+ this.addEntry(map, KEY_SOH, new IntegerDoc().unit(PERCENT));
+ this.addEntry(map, KEY_MAX_CELL_VOLTAGE_ID, new IntegerDoc().unit(NONE));
+ this.addEntry(map, KEY_MAX_CELL_VOLTAGE, new IntegerDoc().unit(MILLIVOLT));
+ this.addEntry(map, KEY_MIN_CELL_VOLTAGE_ID, new IntegerDoc().unit(NONE));
+ this.addEntry(map, KEY_MIN_CELL_VOLTAGE, new IntegerDoc().unit(MILLIVOLT));
+ this.addEntry(map, KEY_MAX_CELL_TEMPERATURE_ID, new IntegerDoc().unit(NONE));
+ this.addEntry(map, KEY_MAX_CELL_TEMPERATURE, new IntegerDoc().unit(DEZIDEGREE_CELSIUS));
+ this.addEntry(map, KEY_MIN_CELL_TEMPERATURE_ID, new IntegerDoc().unit(NONE));
+ this.addEntry(map, KEY_MIN_CELL_TEMPERATURE, new IntegerDoc().unit(DEZIDEGREE_CELSIUS));
+ this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_LOW, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Discharge Temperature Low Alarm Level 2")); /* Bit 15 */
- this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH, Doc.of(Level.FAULT)
+ this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_DISCHA_TEMP_HIGH, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Discharge Temperature High Alarm Level 2")); /* Bit 14 */
this.addEntry(map, KEY_ALARM_LEVEL_2_GR_TEMPERATURE_HIGH,
- Doc.of(Level.FAULT).text("Rack" + this.rackNumber + " GR Temperature High Alarm Level 2")); /* Bit 10 */
- this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW, Doc.of(Level.FAULT)
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " GR Temperature High Alarm Level 2")); /* Bit 10 */
+ this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_LOW, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Charge Temperature Low Alarm Level 2")); /* Bit 7 */
- this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH, Doc.of(Level.FAULT)
+ this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_CHA_TEMP_HIGH, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Charge Temperature High Alarm Level 2")); /* Bit 6 */
- this.addEntry(map, KEY_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH, Doc.of(Level.FAULT)
- .text("Rack" + this.rackNumber + " Discharge Current High Alarm Level 2")); /* Bit 5 */
+ this.addEntry(map, KEY_ALARM_LEVEL_2_DISCHA_CURRENT_HIGH,
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Discharge Current High Alarm Level 2")); /* Bit 5 */
this.addEntry(map, KEY_ALARM_LEVEL_2_TOTAL_VOLTAGE_LOW,
- Doc.of(Level.FAULT).text("Rack" + this.rackNumber + " Total Voltage Low Alarm Level 2")); /* Bit 4 */
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Total Voltage Low Alarm Level 2")); /* Bit 4 */
this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_VOLTAGE_LOW,
- Doc.of(Level.FAULT).text("Cluster 1 Cell Voltage Low Alarm Level 2")); /* Bit 3 */
+ Doc.of(WARNING).text("Cluster 1 Cell Voltage Low Alarm Level 2")); /* Bit 3 */
this.addEntry(map, KEY_ALARM_LEVEL_2_CHA_CURRENT_HIGH,
- Doc.of(Level.FAULT).text("Rack" + this.rackNumber + " Charge Current High Alarm Level 2")); /* Bit 2 */
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Charge Current High Alarm Level 2")); /* Bit 2 */
this.addEntry(map, KEY_ALARM_LEVEL_2_TOTAL_VOLTAGE_HIGH,
- Doc.of(Level.FAULT).text("Rack" + this.rackNumber + " Total Voltage High Alarm Level 2")); /* Bit 1 */
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Total Voltage High Alarm Level 2")); /* Bit 1 */
this.addEntry(map, KEY_ALARM_LEVEL_2_CELL_VOLTAGE_HIGH,
- Doc.of(Level.FAULT).text("Rack" + this.rackNumber + " Cell Voltage High Alarm Level 2")); /* Bit 0 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW, Doc.of(Level.WARNING)
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Cell Voltage High Alarm Level 2")); /* Bit 0 */
+ this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_LOW, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Discharge Temperature Low Alarm Level 1")); /* Bit 15 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH, Doc.of(Level.WARNING)
+ this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_DISCHA_TEMP_HIGH, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Discharge Temperature High Alarm Level 1")); /* Bit 14 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH, Doc.of(Level.WARNING)
- .text("Rack" + this.rackNumber + " Total Voltage Diff High Alarm Level 1")); /* Bit 13 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH, Doc.of(Level.WARNING)
- .text("Rack" + this.rackNumber + " Cell Voltage Diff High Alarm Level 1")); /* Bit 11 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_GR_TEMPERATURE_HIGH, Doc.of(Level.WARNING)
- .text("Rack" + this.rackNumber + " GR Temperature High Alarm Level 1")); /* Bit 10 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH, Doc.of(Level.WARNING)
+ this.addEntry(map, KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_DIFF_HIGH,
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Total Voltage Diff High Alarm Level 1")); /* Bit 13 */
+ this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_VOLTAGE_DIFF_HIGH,
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Cell Voltage Diff High Alarm Level 1")); /* Bit 11 */
+ this.addEntry(map, KEY_ALARM_LEVEL_1_GR_TEMPERATURE_HIGH,
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " GR Temperature High Alarm Level 1")); /* Bit 10 */
+ this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_TEMP_DIFF_HIGH, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell temperature Diff High Alarm Level 1")); /* Bit 9 */
this.addEntry(map, KEY_ALARM_LEVEL_1_SOC_LOW,
- Doc.of(Level.WARNING).text("Rack" + this.rackNumber + " SOC Low Alarm Level 1")); /* Bit 8 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW, Doc.of(Level.WARNING)
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " SOC Low Alarm Level 1")); /* Bit 8 */
+ this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_LOW, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Charge Temperature Low Alarm Level 1")); /* Bit 7 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH, Doc.of(Level.WARNING)
+ this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_CHA_TEMP_HIGH, Doc.of(WARNING)
.text("Rack" + this.rackNumber + " Cell Charge Temperature High Alarm Level 1")); /* Bit 6 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH, Doc.of(Level.WARNING)
- .text("Rack" + this.rackNumber + " Discharge Current High Alarm Level 1")); /* Bit 5 */
+ this.addEntry(map, KEY_ALARM_LEVEL_1_DISCHA_CURRENT_HIGH,
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Discharge Current High Alarm Level 1")); /* Bit 5 */
this.addEntry(map, KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_LOW,
- Doc.of(Level.WARNING).text("Rack" + this.rackNumber + " Total Voltage Low Alarm Level 1")); /* Bit 4 */
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Total Voltage Low Alarm Level 1")); /* Bit 4 */
this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_VOLTAGE_LOW,
- Doc.of(Level.WARNING).text("Rack" + this.rackNumber + " Cell Voltage Low Alarm Level 1")); /* Bit 3 */
- this.addEntry(map, KEY_ALARM_LEVEL_1_CHA_CURRENT_HIGH, Doc.of(Level.WARNING)
- .text("Rack" + this.rackNumber + " Charge Current High Alarm Level 1")); /* Bit 2 */
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Cell Voltage Low Alarm Level 1")); /* Bit 3 */
+ this.addEntry(map, KEY_ALARM_LEVEL_1_CHA_CURRENT_HIGH,
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Charge Current High Alarm Level 1")); /* Bit 2 */
this.addEntry(map, KEY_ALARM_LEVEL_1_TOTAL_VOLTAGE_HIGH,
- Doc.of(Level.WARNING).text("Rack" + this.rackNumber + " Total Voltage High Alarm Level 1")); /* Bit 1 */
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Total Voltage High Alarm Level 1")); /* Bit 1 */
this.addEntry(map, KEY_ALARM_LEVEL_1_CELL_VOLTAGE_HIGH,
- Doc.of(Level.WARNING).text("Rack" + this.rackNumber + " Cell Voltage High Alarm Level 1")); /* Bit 0 */
+ Doc.of(WARNING).text("Rack" + this.rackNumber + " Cell Voltage High Alarm Level 1")); /* Bit 0 */
this.addEntry(map, KEY_RUN_STATE, Doc.of(Enums.ClusterRunState.values())); //
- this.addEntry(map, KEY_FAILURE_INITIALIZATION, Doc.of(Level.FAULT).text("Initialization failure")); /* Bit */
- this.addEntry(map, KEY_FAILURE_EEPROM, Doc.of(Level.FAULT).text("EEPROM fault")); /* Bit 11 */
+ this.addEntry(map, KEY_FAILURE_INITIALIZATION, Doc.of(WARNING).text("Initialization failure")); /* Bit */
+ this.addEntry(map, KEY_FAILURE_EEPROM, Doc.of(WARNING).text("EEPROM fault")); /* Bit 11 */
this.addEntry(map, KEY_FAILURE_INTRANET_COMMUNICATION,
- Doc.of(Level.FAULT).text("Internal communication fault")); /* Bit 10 */
+ Doc.of(WARNING).text("Internal communication fault")); /* Bit 10 */
this.addEntry(map, KEY_FAILURE_TEMPERATURE_SENSOR_CABLE,
- Doc.of(Level.FAULT).text("Temperature sensor cable fault")); /* Bit 9 */
- this.addEntry(map, KEY_FAILURE_BALANCING_MODULE, Doc.of(Level.OK).text("Balancing module fault")); /* Bit 8 */
- this.addEntry(map, KEY_FAILURE_TEMPERATURE_PCB, Doc.of(Level.FAULT).text("Temperature PCB error")); /* Bit 7 */
- this.addEntry(map, KEY_FAILURE_GR_TEMPERATURE, Doc.of(Level.FAULT).text("GR Temperature error")); /* Bit 6 */
- this.addEntry(map, KEY_FAILURE_TEMP_SENSOR, Doc.of(Level.FAULT).text("Temperature sensor fault")); /* Bit 5 */
- this.addEntry(map, KEY_FAILURE_TEMP_SAMPLING,
- Doc.of(Level.FAULT).text("Temperature sampling fault")); /* Bit 4 */
- this.addEntry(map, KEY_FAILURE_VOLTAGE_SAMPLING,
- Doc.of(Level.FAULT).text("Voltage sampling fault")); /* Bit 3 */
- this.addEntry(map, KEY_FAILURE_LTC6803, Doc.of(Level.FAULT).text("LTC6803 fault")); /* Bit 2 */
- this.addEntry(map, KEY_FAILURE_CONNECTOR_WIRE, Doc.of(Level.FAULT).text("connector wire fault")); /* Bit 1 */
- this.addEntry(map, KEY_FAILURE_SAMPLING_WIRE, Doc.of(Level.FAULT).text("sampling wire fault")); /* Bit 0 */
- this.addEntry(map, KEY_SLEEP, Doc.of(OpenemsType.INTEGER).accessMode(AccessMode.READ_WRITE));
- this.addEntry(map, KEY_RESET, Doc.of(OpenemsType.INTEGER).accessMode(AccessMode.READ_WRITE));
+ Doc.of(WARNING).text("Temperature sensor cable fault")); /* Bit 9 */
+ this.addEntry(map, KEY_FAILURE_BALANCING_MODULE, Doc.of(OK).text("Balancing module fault")); /* Bit 8 */
+ this.addEntry(map, KEY_FAILURE_TEMPERATURE_PCB, Doc.of(WARNING).text("Temperature PCB error")); /* Bit 7 */
+ this.addEntry(map, KEY_FAILURE_GR_TEMPERATURE, Doc.of(WARNING).text("GR Temperature error")); /* Bit 6 */
+ this.addEntry(map, KEY_FAILURE_TEMP_SENSOR, Doc.of(WARNING).text("Temperature sensor fault")); /* Bit 5 */
+ this.addEntry(map, KEY_FAILURE_TEMP_SAMPLING, Doc.of(WARNING).text("Temperature sampling fault")); /* Bit 4 */
+ this.addEntry(map, KEY_FAILURE_VOLTAGE_SAMPLING, Doc.of(WARNING).text("Voltage sampling fault")); /* Bit 3 */
+ this.addEntry(map, KEY_FAILURE_LTC6803, Doc.of(WARNING).text("LTC6803 fault")); /* Bit 2 */
+ this.addEntry(map, KEY_FAILURE_CONNECTOR_WIRE, Doc.of(WARNING).text("connector wire fault")); /* Bit 1 */
+ this.addEntry(map, KEY_FAILURE_SAMPLING_WIRE, Doc.of(WARNING).text("sampling wire fault")); /* Bit 0 */
+ this.addEntry(map, KEY_SLEEP, Doc.of(INTEGER).accessMode(READ_WRITE));
+ this.addEntry(map, KEY_RESET, Doc.of(INTEGER).accessMode(READ_WRITE));
// Cell voltages formatted like: "RACK_1_BATTERY_000_VOLTAGE"
for (var i = 0; i < this.numberOfSlaves; i++) {
for (var j = i * VOLTAGE_SENSORS_PER_MODULE; j < (i + 1) * VOLTAGE_SENSORS_PER_MODULE; j++) {
var key = this.getSingleCellPrefix(j) + "_" + VOLTAGE;
- this.addEntry(map, key, new IntegerDoc().unit(Unit.MILLIVOLT));
+ this.addEntry(map, key, new IntegerDoc().unit(MILLIVOLT));
}
}
// Cell temperatures formatted like : "RACK_1_BATTERY_000_TEMPERATURE"
for (var i = 0; i < this.numberOfSlaves; i++) {
for (var j = i * TEMPERATURE_SENSORS_PER_MODULE; j < (i + 1) * TEMPERATURE_SENSORS_PER_MODULE; j++) {
var key = this.getSingleCellPrefix(j) + "_" + TEMPERATURE;
- this.addEntry(map, key, new IntegerDoc().unit(Unit.DEZIDEGREE_CELSIUS));
+ this.addEntry(map, key, new IntegerDoc().unit(DEZIDEGREE_CELSIUS));
}
}
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionC.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionC.java
index 99f8fea4c9f..cec303bdbf7 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionC.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionC.java
@@ -265,82 +265,82 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId
// Master BMS Alarm Registers
MASTER_EMS_COMMUNICATION_FAILURE(Doc.of(Level.WARNING) //
.text("Master EMS Communication Failure")),
- MASTER_PCS_CONTROL_FAILURE(Doc.of(Level.FAULT) //
+ MASTER_PCS_CONTROL_FAILURE(Doc.of(Level.WARNING) //
.text("Master PCS Control Failure")),
- MASTER_PCS_COMMUNICATION_FAILURE(Doc.of(Level.FAULT) //
+ MASTER_PCS_COMMUNICATION_FAILURE(Doc.of(Level.WARNING) //
.text("Master PCS Communication Failure")),
// Rack #1 cannot be paralleled to DC Bus reasons
- RACK_1_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_1_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 1 Level 2 Alarm")),
- RACK_1_PCS_CONTROL_FAILURE(Doc.of(Level.FAULT) //
+ RACK_1_PCS_CONTROL_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 1 PCS Control Failure")),
- RACK_1_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.FAULT) //
+ RACK_1_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 1 Communication to Master BMS Failure")),
- RACK_1_HARDWARE_FAILURE(Doc.of(Level.FAULT) //
+ RACK_1_HARDWARE_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 1 Hardware Failure")),
- RACK_1_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_1_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 1 Too big circulating Current among clusters (>4A)")),
- RACK_1_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_1_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 1 Too big boltage difference among clusters (>50V)")),
// Rack #2 cannot be paralleled to DC Bus reasons
- RACK_2_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_2_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 2 Level 2 Alarm")),
- RACK_2_PCS_CONTROL_FAILURE(Doc.of(Level.FAULT) //
+ RACK_2_PCS_CONTROL_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 2 PCS Control Failure")),
- RACK_2_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.FAULT) //
+ RACK_2_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 2 Communication to Master BMS Failure")),
- RACK_2_HARDWARE_FAILURE(Doc.of(Level.FAULT) //
+ RACK_2_HARDWARE_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 2 Hardware Failure")),
- RACK_2_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_2_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 2 Too big circulating Current among clusters (>4A)")),
- RACK_2_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_2_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 2 Too big boltage difference among clusters (>50V)")),
// Rack #3 cannot be paralleled to DC Bus reasons
- RACK_3_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_3_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 3 Level 2 Alarm")),
- RACK_3_PCS_CONTROL_FAILURE(Doc.of(Level.FAULT) //
+ RACK_3_PCS_CONTROL_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 3 PCS Control Failure")),
- RACK_3_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.FAULT) //
+ RACK_3_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 3 Communication to Master BMS Failure")),
- RACK_3_HARDWARE_FAILURE(Doc.of(Level.FAULT) //
+ RACK_3_HARDWARE_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 3 Hardware Failure")),
- RACK_3_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_3_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 3 Too big circulating Current among clusters (>4A)")),
- RACK_3_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_3_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 3 Too big boltage difference among clusters (>50V)")),
// Rack #4 cannot be paralleled to DC Bus reasons
- RACK_4_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_4_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 4 Level 2 Alarm")),
- RACK_4_PCS_CONTROL_FAILURE(Doc.of(Level.FAULT) //
+ RACK_4_PCS_CONTROL_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 4 PCS Control Failure")),
- RACK_4_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.FAULT) //
+ RACK_4_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 4 Communication to Master BMS Failure")),
- RACK_4_HARDWARE_FAILURE(Doc.of(Level.FAULT) //
+ RACK_4_HARDWARE_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 4 Hardware Failure")),
- RACK_4_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_4_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 4 Too big circulating Current among clusters (>4A)")),
- RACK_4_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_4_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 4 Too big boltage difference among clusters (>50V)")),
// Rack #5 cannot be paralleled to DC Bus reasons
- RACK_5_LEVEL_2_ALARM(Doc.of(Level.FAULT) //
+ RACK_5_LEVEL_2_ALARM(Doc.of(Level.WARNING) //
.text("Rack 5 Level 2 Alarm")),
- RACK_5_PCS_CONTROL_FAILURE(Doc.of(Level.FAULT) //
+ RACK_5_PCS_CONTROL_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 5 PCS Control Failure")),
- RACK_5_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.FAULT) //
+ RACK_5_COMMUNICATION_TO_MASTER_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 5 Communication to Master BMS Failure")),
- RACK_5_HARDWARE_FAILURE(Doc.of(Level.FAULT) //
+ RACK_5_HARDWARE_FAILURE(Doc.of(Level.WARNING) //
.text("Rack 5 Hardware Failure")),
- RACK_5_OVER_CURRENT(Doc.of(Level.FAULT) //
+ RACK_5_OVER_CURRENT(Doc.of(Level.WARNING) //
.text("Rack 5 Too big circulating Current among clusters (>4A)")),
- RACK_5_VOLTAGE_DIFFERENCE(Doc.of(Level.FAULT) //
+ RACK_5_VOLTAGE_DIFFERENCE(Doc.of(Level.WARNING) //
.text("Rack 5 Too big boltage difference among clusters (>50V)")),
// OpenEMS Faults
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
- MAX_START_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_START_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of start attempts failed")), //
- MAX_STOP_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_STOP_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of stop attempts failed")), //
NUMBER_OF_MODULES_PER_TOWER(Doc.of(OpenemsType.INTEGER) //
.persistencePriority(PersistencePriority.HIGH) //
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java
index 56c3d916474..6b493c42f7a 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java
@@ -3,6 +3,7 @@
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce;
+import static io.openems.edge.bridge.modbus.api.ModbusUtils.FunctionCode.FC3;
import java.util.LinkedList;
import java.util.Optional;
@@ -648,7 +649,7 @@ private void calculateCapacity(int numberOfTowers, int numberOfModules) {
* @throws OpenemsException on error
*/
private CompletableFuture getNumberOfModules() {
- return readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull,
+ return readElementOnce(FC3, this.getModbusProtocol(), ModbusUtils::retryOnNull,
new UnsignedWordElement(0x20C1 /* No of modules for 1st tower */));
}
@@ -675,7 +676,7 @@ private void checkNumberOfTowers(BiPredicate retryPredica
}
// Read next address in Queue
- readElementOnce(this.getModbusProtocol(), retryPredicate, new UnsignedWordElement(address))
+ readElementOnce(FC3, this.getModbusProtocol(), retryPredicate, new UnsignedWordElement(address))
.thenAccept(numberOfModules -> {
if (numberOfModules == null) {
// Read error -> this tower does not exist. Stop here.
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/RackChannel.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/RackChannel.java
index ec13258ac7c..daee5253fa6 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/RackChannel.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/RackChannel.java
@@ -463,27 +463,27 @@ public enum RackChannel {
LEVEL1_TOTAL_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("Total Voltage High Alarm Level 1")), //
// Alarm Level 2
- LEVEL2_DISCHARGE_TEMP_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_TEMP_LOW(Doc.of(Level.WARNING) //
.text("Discharge Temperature Low Alarm Level 2")), //
- LEVEL2_DISCHARGE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Discharge Temperature High Alarm Level 2")), //
- LEVEL2_INSULATION_VALUE(Doc.of(Level.FAULT) //
+ LEVEL2_INSULATION_VALUE(Doc.of(Level.WARNING) //
.text("Insulation Value Failure Alarm Level 2")), //
- LEVEL2_POWER_POLE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_POWER_POLE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Power Pole temperature too high Alarm Level 2")), //
- LEVEL2_CHARGE_TEMP_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_TEMP_LOW(Doc.of(Level.WARNING) //
.text("Cell Charge Temperature Low Alarm Level 2")), //
- LEVEL2_CHARGE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Charge Temperature High Alarm Level 2")), //
- LEVEL2_DISCHARGE_CURRENT_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_CURRENT_HIGH(Doc.of(Level.WARNING) //
.text("Discharge Current High Alarm Level 2")), //
- LEVEL2_TOTAL_VOLTAGE_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_TOTAL_VOLTAGE_LOW(Doc.of(Level.WARNING) //
.text("Total Voltage Low Alarm Level 2")), //
- LEVEL2_CELL_VOLTAGE_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_CELL_VOLTAGE_LOW(Doc.of(Level.WARNING) //
.text("Cell Voltage Low Alarm Level 2")), //
- LEVEL2_CHARGE_CURRENT_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_CURRENT_HIGH(Doc.of(Level.WARNING) //
.text("Charge Current High Alarm Level 2")), //
- LEVEL2_TOTAL_VOLTAGE_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_TOTAL_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("Total Voltage High Alarm Level 2")), //
LEVEL2_CELL_VOLTAGE_HIGH(Doc.of(Level.INFO) //
.text("Cell Voltage High Alarm Level 2")), //
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionA.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionA.java
index 9ee4f1c6abf..7d65c2aacd1 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionA.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionA.java
@@ -681,29 +681,29 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
.text("Cluster 1 Total Voltage High Alarm Level 1")), //
ALARM_LEVEL_1_CELL_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("Cluster 1 Cell Voltage High Alarm Level 1")), //
- FAILURE_INITIALIZATION(Doc.of(Level.FAULT) //
+ FAILURE_INITIALIZATION(Doc.of(Level.WARNING) //
.text("Initialization failure")), //
- FAILURE_EEPROM(Doc.of(Level.FAULT) //
+ FAILURE_EEPROM(Doc.of(Level.WARNING) //
.text("EEPROM fault")), //
- FAILURE_INTRANET_COMMUNICATION(Doc.of(Level.FAULT) //
+ FAILURE_INTRANET_COMMUNICATION(Doc.of(Level.WARNING) //
.text("Intranet communication fault")), //
- FAILURE_TEMP_SAMPLING_LINE(Doc.of(Level.FAULT) //
+ FAILURE_TEMP_SAMPLING_LINE(Doc.of(Level.WARNING) //
.text("Temperature sampling line fault")), //
- FAILURE_BALANCING_MODULE(Doc.of(Level.FAULT) //
+ FAILURE_BALANCING_MODULE(Doc.of(Level.WARNING) //
.text("Balancing module fault")), //
- FAILURE_TEMP_SENSOR(Doc.of(Level.FAULT) //
+ FAILURE_TEMP_SENSOR(Doc.of(Level.WARNING) //
.text("Temperature sensor fault")), //
- FAILURE_TEMP_SAMPLING(Doc.of(Level.FAULT) //
+ FAILURE_TEMP_SAMPLING(Doc.of(Level.WARNING) //
.text("Temperature sampling fault")), //
- FAILURE_VOLTAGE_SAMPLING(Doc.of(Level.FAULT) //
+ FAILURE_VOLTAGE_SAMPLING(Doc.of(Level.WARNING) //
.text("Voltage sampling fault")), //
- FAILURE_LTC6803(Doc.of(Level.FAULT) //
+ FAILURE_LTC6803(Doc.of(Level.WARNING) //
.text("LTC6803 fault")), //
FAILURE_CONNECTOR_WIRE(Doc.of(Level.WARNING) //
.text("connector wire fault")), //
- FAILURE_SAMPLING_WIRE(Doc.of(Level.FAULT) //
+ FAILURE_SAMPLING_WIRE(Doc.of(Level.WARNING) //
.text("sampling wire fault")), //
- PRECHARGE_TAKING_TOO_LONG(Doc.of(Level.FAULT) //
+ PRECHARGE_TAKING_TOO_LONG(Doc.of(Level.WARNING) //
.text("precharge time was too long")), //
STATE_MACHINE(Doc.of(State.values()) //
.text("Current State of State-Machine")), //
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionB.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionB.java
index ecc741e2cc2..8434481d85c 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionB.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionB.java
@@ -995,11 +995,11 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId
.text("precharge time was too long")),
// OpenEMS Faults
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
- MAX_START_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_START_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of start attempts failed")), //
- MAX_STOP_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_STOP_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of stop attempts failed")), //
;
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java
index 2bc1e0c7db0..a87a7fe5106 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java
@@ -5,6 +5,7 @@
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce;
+import static io.openems.edge.bridge.modbus.api.ModbusUtils.FunctionCode.FC3;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@@ -917,7 +918,8 @@ protected ModbusProtocol defineModbusProtocol() {
* @return the Number of Modules as a {@link CompletableFuture}.
*/
private CompletableFuture getNumberOfModules() {
- return readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull, new UnsignedWordElement(0x20C1));
+ return readElementOnce(FC3, this.getModbusProtocol(), ModbusUtils::retryOnNull,
+ new UnsignedWordElement(0x20C1));
}
/**
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionC.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionC.java
index 28a54d5b9af..1f57a137c60 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionC.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionC.java
@@ -514,29 +514,29 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId
// Faults and warnings
// Alarm Level 2
- LEVEL2_DISCHARGE_TEMP_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_TEMP_LOW(Doc.of(Level.WARNING) //
.text("Discharge Temperature Low Alarm Level 2")), //
- LEVEL2_DISCHARGE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Discharge Temperature High Alarm Level 2")), //
- LEVEL2_INSULATION_VALUE(Doc.of(Level.FAULT) //
+ LEVEL2_INSULATION_VALUE(Doc.of(Level.WARNING) //
.text("Insulation Value Failure Alarm Level 2")), //
- LEVEL2_POWER_POLE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_POWER_POLE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Power Pole temperature too high Alarm Level 2")), //
- LEVEL2_CHARGE_TEMP_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_TEMP_LOW(Doc.of(Level.WARNING) //
.text("Cell Charge Temperature Low Alarm Level 2")), //
- LEVEL2_CHARGE_TEMP_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_TEMP_HIGH(Doc.of(Level.WARNING) //
.text("Charge Temperature High Alarm Level 2")), //
- LEVEL2_DISCHARGE_CURRENT_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_DISCHARGE_CURRENT_HIGH(Doc.of(Level.WARNING) //
.text("Discharge Current High Alarm Level 2")), //
- LEVEL2_TOTAL_VOLTAGE_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_TOTAL_VOLTAGE_LOW(Doc.of(Level.WARNING) //
.text("Total Voltage Low Alarm Level 2")), //
- LEVEL2_CELL_VOLTAGE_LOW(Doc.of(Level.FAULT) //
+ LEVEL2_CELL_VOLTAGE_LOW(Doc.of(Level.WARNING) //
.text("Cell Voltage Low Alarm Level 2")), //
- LEVEL2_CHARGE_CURRENT_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CHARGE_CURRENT_HIGH(Doc.of(Level.WARNING) //
.text("Charge Current High Alarm Level 2")), //
- LEVEL2_TOTAL_VOLTAGE_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_TOTAL_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("Total Voltage High Alarm Level 2")), //
- LEVEL2_CELL_VOLTAGE_HIGH(Doc.of(Level.FAULT) //
+ LEVEL2_CELL_VOLTAGE_HIGH(Doc.of(Level.WARNING) //
.text("Cell Voltage High Alarm Level 2")), //
// Alarm Level 1
@@ -672,11 +672,11 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId
.text("Slave 20 communication error")), //
// OpenEMS Faults
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
- MAX_START_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_START_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of start attempts failed")), //
- MAX_STOP_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_STOP_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of stop attempts failed")), //
;
diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java
index 69feb15eeaa..988a530da27 100644
--- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java
+++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java
@@ -4,6 +4,7 @@
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2;
import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1;
import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce;
+import static io.openems.edge.bridge.modbus.api.ModbusUtils.FunctionCode.FC3;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
@@ -154,7 +155,8 @@ private void calculateCapacity(Integer numberOfModules) {
* @return the Number of Modules as a {@link CompletableFuture}.
*/
private CompletableFuture getNumberOfModules() {
- return readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull, new UnsignedWordElement(0x20C1));
+ return readElementOnce(FC3, this.getModbusProtocol(), ModbusUtils::retryOnNull,
+ new UnsignedWordElement(0x20C1));
}
@Override
diff --git a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImplTest.java b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImplTest.java
index eda7ce07667..be7f6db76ce 100644
--- a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImplTest.java
+++ b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImplTest.java
@@ -1,9 +1,13 @@
package io.openems.edge.battery.soltaro.cluster.versionb;
+import static io.openems.edge.battery.soltaro.cluster.SoltaroCluster.ChannelId.SUB_MASTER_1_COMMUNICATION_FAILURE;
+import static io.openems.edge.battery.soltaro.cluster.SoltaroCluster.ChannelId.SUB_MASTER_2_COMMUNICATION_FAILURE;
+import static io.openems.edge.battery.soltaro.cluster.SoltaroCluster.ChannelId.SUB_MASTER_3_COMMUNICATION_FAILURE;
+import static io.openems.edge.battery.soltaro.cluster.SoltaroCluster.ChannelId.SUB_MASTER_4_COMMUNICATION_FAILURE;
+import static io.openems.edge.battery.soltaro.cluster.SoltaroCluster.ChannelId.SUB_MASTER_5_COMMUNICATION_FAILURE;
+
import org.junit.Test;
-import io.openems.common.types.ChannelAddress;
-import io.openems.edge.battery.soltaro.cluster.SoltaroCluster;
import io.openems.edge.battery.soltaro.common.enums.BatteryState;
import io.openems.edge.battery.soltaro.common.enums.ModuleType;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
@@ -14,30 +18,16 @@
public class BatterySoltaroClusterVersionBImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
- private static final ChannelAddress SUB_MASTER_1_COMMUNICATION_FAILURE = new ChannelAddress(BATTERY_ID,
- SoltaroCluster.ChannelId.SUB_MASTER_1_COMMUNICATION_FAILURE.id());
- private static final ChannelAddress SUB_MASTER_2_COMMUNICATION_FAILURE = new ChannelAddress(BATTERY_ID,
- SoltaroCluster.ChannelId.SUB_MASTER_2_COMMUNICATION_FAILURE.id());
- private static final ChannelAddress SUB_MASTER_3_COMMUNICATION_FAILURE = new ChannelAddress(BATTERY_ID,
- SoltaroCluster.ChannelId.SUB_MASTER_3_COMMUNICATION_FAILURE.id());
- private static final ChannelAddress SUB_MASTER_4_COMMUNICATION_FAILURE = new ChannelAddress(BATTERY_ID,
- SoltaroCluster.ChannelId.SUB_MASTER_4_COMMUNICATION_FAILURE.id());
- private static final ChannelAddress SUB_MASTER_5_COMMUNICATION_FAILURE = new ChannelAddress(BATTERY_ID,
- SoltaroCluster.ChannelId.SUB_MASTER_5_COMMUNICATION_FAILURE.id());
-
@Test
public void test() throws Exception {
var sut = new BatterySoltaroClusterVersionBImpl();
new ComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager())
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setNumberOfSlaves(0) //
.setModuleType(ModuleType.MODULE_3_5_KWH) //
diff --git a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImplTest.java b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImplTest.java
index aaffbc2cc9e..ebcb21122ce 100644
--- a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImplTest.java
+++ b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImplTest.java
@@ -9,17 +9,14 @@
public class BatterySoltaroClusterVersionCImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
@Test
public void test() throws Exception {
new ComponentTest(new BatterySoltaroClusterVersionCImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setStartStop(StartStopConfig.AUTO) //
.build()) //
;
diff --git a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImplTest.java b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImplTest.java
index 443dbce24c9..daccfa35f66 100644
--- a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImplTest.java
+++ b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImplTest.java
@@ -9,17 +9,14 @@
public class BatterySoltaroSingleRackVersionAImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
@Test
public void test() throws Exception {
new ComponentTest(new BatterySoltaroSingleRackVersionAImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setErrorLevel2Delay(0) //
.setMaxStartTime(0) //
diff --git a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImplTest.java b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImplTest.java
index b2b33ff92fb..3fce33da342 100644
--- a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImplTest.java
+++ b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImplTest.java
@@ -11,18 +11,15 @@
public class BatterySoltaroSingleRackVersionBImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
@Test
public void test() throws Exception {
new ComponentTest(new BatterySoltaroSingleRackVersionBImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setErrorLevel2Delay(0) //
.setMaxStartTime(0) //
diff --git a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImplTest.java b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImplTest.java
index 82e9a8c75d9..504d8d66b2a 100644
--- a/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImplTest.java
+++ b/io.openems.edge.battery.soltaro/test/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImplTest.java
@@ -9,17 +9,14 @@
public class BatterySoltaroSingleRackVersionCImplTest {
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
@Test
public void test() throws Exception {
new ComponentTest(new BatterySoltaroSingleRackVersionCImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("battery0") //
+ .setModbusId("modbus0") //
.setModbusUnitId(0) //
.setStartStop(StartStopConfig.AUTO) //
.build()) //
diff --git a/io.openems.edge.batteryinverter.api/.classpath b/io.openems.edge.batteryinverter.api/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.batteryinverter.api/.classpath
+++ b/io.openems.edge.batteryinverter.api/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/.classpath b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/.classpath
+++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java
index d9124765b01..b32ba021cd3 100644
--- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java
+++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java
@@ -33,29 +33,40 @@ public interface BatteryInverterKacoBlueplanetGridsave extends ManagedSymmetricB
public static final int WATCHDOG_TRIGGER_SECONDS = 10;
public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
+
+ /*
+ * Whenever one of these states would be Level.FAULT, the EssGeneric will stop
+ * the battery and the inverter. If this is necessary, it must be specifically
+ * mentioned and the state should have a proper description of the fault.
+ */
+
STATE_MACHINE(Doc.of(State.values()) //
.text("Current State of State-Machine")), //
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
- MAX_START_TIMEOUT(Doc.of(Level.FAULT) //
+ MAX_START_TIMEOUT(Doc.of(Level.WARNING) //
.text("Max start time is exceeded")), //
- MAX_STOP_TIMEOUT(Doc.of(Level.FAULT) //
+ MAX_STOP_TIMEOUT(Doc.of(Level.WARNING) //
.text("Max stop time is exceeded")), //
- INVERTER_CURRENT_STATE_FAULT(Doc.of(Level.FAULT) //
+
+ /**
+ * Internal StateMachine from KACO.
+ */
+ INVERTER_CURRENT_STATE_FAULT(Doc.of(Level.WARNING) //
.text("The 'CurrentState' is invalid")), //
- GRID_DISCONNECTION(Doc.of(Level.FAULT) //
+ GRID_DISCONNECTION(Doc.of(Level.WARNING) //
.text("External grid protection disconnection (17)")), //
- GRID_FAILURE_LINE_TO_LINE(Doc.of(Level.FAULT) //
+ GRID_FAILURE_LINE_TO_LINE(Doc.of(Level.WARNING) //
.text("Grid failure phase-to-phase voltage (47)")), //
- LINE_FAILURE_UNDER_FREQ(Doc.of(Level.FAULT) //
+ LINE_FAILURE_UNDER_FREQ(Doc.of(Level.WARNING) //
.text("Line failure: Grid frequency is too low (48)")), //
- LINE_FAILURE_OVER_FREQ(Doc.of(Level.FAULT) //
+ LINE_FAILURE_OVER_FREQ(Doc.of(Level.WARNING) //
.text("Line failure: Grid frequency is too high (49)")), //
- PROTECTION_SHUTDOWN_LINE_1(Doc.of(Level.FAULT) //
+ PROTECTION_SHUTDOWN_LINE_1(Doc.of(Level.WARNING) //
.text("Grid Failure: grid voltage L1 protection (81)")), //
- PROTECTION_SHUTDOWN_LINE_2(Doc.of(Level.FAULT) //
+ PROTECTION_SHUTDOWN_LINE_2(Doc.of(Level.WARNING) //
.text("Grid Failure: grid voltage L2 protection (82)")), //
- PROTECTION_SHUTDOWN_LINE_3(Doc.of(Level.FAULT) //
+ PROTECTION_SHUTDOWN_LINE_3(Doc.of(Level.WARNING) //
.text("Grid Failure: grid voltage L3 protection (83)")), //
;
diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java
index 84ec0153305..9b5a3cefef3 100644
--- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java
+++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java
@@ -28,6 +28,7 @@
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
+import io.openems.common.channel.AccessMode;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;
import io.openems.common.types.OptionsEnum;
@@ -57,6 +58,9 @@
import io.openems.edge.common.channel.value.Value;
import io.openems.edge.common.component.ComponentManager;
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.startstop.StartStop;
import io.openems.edge.common.startstop.StartStoppable;
import io.openems.edge.common.taskmanager.Priority;
@@ -75,7 +79,7 @@
)
public class BatteryInverterKacoBlueplanetGridsaveImpl extends AbstractSunSpecBatteryInverter
implements BatteryInverterKacoBlueplanetGridsave, ManagedSymmetricBatteryInverter, SymmetricBatteryInverter,
- ModbusComponent, OpenemsComponent, TimedataProvider, StartStoppable {
+ ModbusComponent, ModbusSlave, OpenemsComponent, TimedataProvider, StartStoppable {
private static final int UNIT_ID = 1;
private static final int READ_FROM_MODBUS_BLOCK = 1;
@@ -140,7 +144,6 @@ protected void setModbus(BridgeModbus modbus) {
// .put(SunSpecModel.S_136, Priority.LOW) //
// .put(SunSpecModel.S_160, Priority.LOW) //
- @Activate
public BatteryInverterKacoBlueplanetGridsaveImpl() {
super(//
ACTIVE_MODELS, //
@@ -158,11 +161,11 @@ public BatteryInverterKacoBlueplanetGridsaveImpl() {
@Activate
private void activate(ComponentContext context, Config config) throws OpenemsException {
+ this.config = config;
if (super.activate(context, config.id(), config.alias(), config.enabled(), UNIT_ID, this.cm, "Modbus",
config.modbus_id(), READ_FROM_MODBUS_BLOCK)) {
return;
}
- this.config = config;
}
@Override
@@ -279,6 +282,16 @@ private void handleGridDisconnection() {
});
}
+ @Override
+ public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
+ return new ModbusSlaveTable(//
+ OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
+ SymmetricBatteryInverter.getModbusSlaveNatureTable(accessMode), //
+ ManagedSymmetricBatteryInverter.getModbusSlaveNatureTable(accessMode), //
+ ModbusSlaveNatureTable.of(BatteryInverterKacoBlueplanetGridsave.class, accessMode, 100) //
+ .build());
+ }
+
@Override
public BatteryInverterConstraint[] getStaticConstraints() throws OpenemsException {
if (this.stateMachine.getCurrentState() == State.RUNNING) {
diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java
index 2757dbf6616..751aed62757 100644
--- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java
+++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java
@@ -106,7 +106,7 @@ public static enum S64201 implements SunSpecPoint {
V_AR(new ScaledValuePoint("S64201_V_AR", "AC Reactive Power", "", //
ValuePoint.Type.INT16, true, AccessMode.READ_ONLY, Unit.VOLT_AMPERE_REACTIVE, "V_AR_SF")), //
HZ(new ScaledValuePoint("S64201_HZ", "Line Frequency", "", //
- ValuePoint.Type.INT16, true, AccessMode.READ_ONLY, Unit.MILLIHERTZ, "mHZ_SF")), //
+ ValuePoint.Type.UINT16, true, AccessMode.READ_ONLY, Unit.HERTZ, "Hz_SF")), //
RESERVED_36(new ReservedPoint("S64201_RESERVED_36")), //
RESERVED_37(new ReservedPoint("S64201_RESERVED_37")), //
RESERVED_38(new ReservedPoint("S64201_RESERVED_38")), //
@@ -271,6 +271,7 @@ public static enum S64201StVnd implements OptionsEnum {
LINE_FAILURE_OVERVOLTAGE_3(46,
"Line failure overvoltage L3 The voltage of a grid phase is too low; the grid cannot be fed into. The phase experiencing failure is displayed."), //
GRID_FAILURE_PHASETOPHASE(47, "Grid failure phase-to-phase voltage"), //
+
LINE_FAILURE_UNDERFREQ(48,
"Line failure: underfreq. Grid frequency is too low. This fault may be gridrelated."), //
LINE_FAILURE_OVERFREQ(49,
diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java
index 8dc242a57b6..1424879da21 100644
--- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java
+++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java
@@ -14,11 +14,18 @@ protected void onEntry(Context context) throws OpenemsNamedException {
}
@Override
- public State runAndGetNextState(Context context) throws OpenemsNamedException {
+ public State runAndGetNextState(Context context) {
final var inverter = context.getParent();
if (!inverter.hasFailure()) {
- return State.GO_STOPPED;
+ return State.UNDEFINED;
}
return State.ERROR;
}
+
+ @Override
+ protected void onExit(Context context) {
+ final var inverter = context.getParent();
+ inverter._setMaxStartTimeout(false);
+ inverter._setMaxStopTimeout(false);
+ }
}
diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java
index 4bc316e97c5..d3d8025f5e1 100644
--- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java
+++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java
@@ -1,15 +1,19 @@
package io.openems.edge.batteryinverter.kaco.blueplanetgridsave;
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.temporal.ChronoUnit;
+import static io.openems.common.test.TestUtils.createDummyClock;
+import static io.openems.edge.batteryinverter.api.SymmetricBatteryInverter.ChannelId.MAX_APPARENT_POWER;
+import static io.openems.edge.batteryinverter.kaco.blueplanetgridsave.BatteryInverterKacoBlueplanetGridsave.WATCHDOG_TIMEOUT_SECONDS;
+import static io.openems.edge.batteryinverter.kaco.blueplanetgridsave.BatteryInverterKacoBlueplanetGridsave.WATCHDOG_TRIGGER_SECONDS;
+import static io.openems.edge.batteryinverter.kaco.blueplanetgridsave.BatteryInverterKacoBlueplanetGridsave.ChannelId.STATE_MACHINE;
+import static io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.CURRENT_STATE;
+import static io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.WATCHDOG;
+import static java.time.temporal.ChronoUnit.SECONDS;
import org.junit.Before;
import org.junit.Test;
import io.openems.common.exceptions.OpenemsException;
import io.openems.common.test.TimeLeapClock;
-import io.openems.common.types.ChannelAddress;
import io.openems.edge.battery.api.Battery;
import io.openems.edge.battery.test.DummyBattery;
import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201CurrentState;
@@ -27,22 +31,9 @@
public class BatteryInverterKacoBlueplanetGridsaveImplTest {
- private static final String BATTERY_INVERTER_ID = "batteryInverter0";
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
-
- private static final ChannelAddress STATE_MACHINE = new ChannelAddress(BATTERY_INVERTER_ID, "StateMachine");
-
- private static final ChannelAddress MAX_APPARENT_POWER = new ChannelAddress(BATTERY_INVERTER_ID,
- "MaxApparentPower");
- private static final ChannelAddress CURRENT_STATE = new ChannelAddress(BATTERY_INVERTER_ID,
- KacoSunSpecModel.S64201.CURRENT_STATE.getChannelId().id());
- private static final ChannelAddress WATCHDOG = new ChannelAddress(BATTERY_INVERTER_ID,
- KacoSunSpecModel.S64201.WATCHDOG.getChannelId().id());
-
private static class MyComponentTest extends ComponentTest {
- private final Battery battery = new DummyBattery(BATTERY_ID);
+ private final Battery battery = new DummyBattery("battery0");
public MyComponentTest(OpenemsComponent sut) throws OpenemsException {
super(sut);
@@ -58,20 +49,17 @@ protected void handleEvent(String topic) throws Exception {
}
- private static TimeLeapClock clock;
+ private static final TimeLeapClock CLOCK = createDummyClock();
private static ComponentTest test;
@Before
public void prepareTest() throws Exception {
- final var start = 1577836800L;
- clock = new TimeLeapClock(Instant.ofEpochSecond(start) /* starts at 1. January 2020 00:00:00 */,
- ZoneOffset.UTC);
var sut = new BatteryInverterKacoBlueplanetGridsaveImpl();
test = new MyComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
- .addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID));
+ .addReference("componentManager", new DummyComponentManager(CLOCK)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0"));
// TODO implement proper Dummy-Modbus-Bridge with SunSpec support. Till then...
test.addReference("isSunSpecInitializationCompleted", true); //
@@ -86,16 +74,16 @@ public void prepareTest() throws Exception {
addChannel.invoke(sut, KacoSunSpecModel.S64202.CHA_MAX_A_0.getChannelId());
addChannel.invoke(sut, KacoSunSpecModel.S64202.EN_LIMIT_0.getChannelId());
addChannel.invoke(sut, KacoSunSpecModel.S64201.REQUESTED_STATE.getChannelId());
- addChannel.invoke(sut, KacoSunSpecModel.S64201.CURRENT_STATE.getChannelId());
- addChannel.invoke(sut, KacoSunSpecModel.S64201.WATCHDOG.getChannelId());
+ addChannel.invoke(sut, CURRENT_STATE.getChannelId());
+ addChannel.invoke(sut, WATCHDOG.getChannelId());
addChannel.invoke(sut, KacoSunSpecModel.S64201.W_SET_PCT.getChannelId());
addChannel.invoke(sut, KacoSunSpecModel.S64201.WPARAM_RMP_TMS.getChannelId());
addChannel.invoke(sut, KacoSunSpecModel.S64201.ST_VND.getChannelId());
test.activate(MyConfig.create() //
- .setId(BATTERY_INVERTER_ID) //
+ .setId("batteryInverter0") //
.setStartStopConfig(StartStopConfig.START) //
- .setModbusId(MODBUS_ID) //
+ .setModbusId("modbus0") //
.setActivateWatchdog(true) //
.build()); //
}
@@ -104,16 +92,16 @@ public void prepareTest() throws Exception {
public void testStart() throws Exception {
test //
.next(new TestCase() //
- .input(CURRENT_STATE, S64201CurrentState.STANDBY) //
+ .input(CURRENT_STATE.getChannelId(), S64201CurrentState.STANDBY) //
.input(MAX_APPARENT_POWER, 50_000) //
.output(STATE_MACHINE, State.UNDEFINED)) //
.next(new TestCase() //
- .timeleap(clock, 4, ChronoUnit.SECONDS) //
+ .timeleap(CLOCK, 4, SECONDS) //
.output(STATE_MACHINE, State.GO_RUNNING)) //
.next(new TestCase() //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .input(CURRENT_STATE, S64201CurrentState.GRID_CONNECTED) //
- .output(WATCHDOG, BatteryInverterKacoBlueplanetGridsave.WATCHDOG_TIMEOUT_SECONDS)) //
+ .timeleap(CLOCK, 1, SECONDS) //
+ .input(CURRENT_STATE.getChannelId(), S64201CurrentState.GRID_CONNECTED) //
+ .output(WATCHDOG.getChannelId(), WATCHDOG_TIMEOUT_SECONDS)) //
.next(new TestCase() //
.output(STATE_MACHINE, State.RUNNING)) //
;
@@ -123,14 +111,13 @@ public void testStart() throws Exception {
public void testWatchdog() throws Exception {
test //
.next(new TestCase() //
- .output(WATCHDOG, BatteryInverterKacoBlueplanetGridsave.WATCHDOG_TIMEOUT_SECONDS)) //
+ .output(WATCHDOG.getChannelId(), WATCHDOG_TIMEOUT_SECONDS)) //
.next(new TestCase() //
- .timeleap(clock, BatteryInverterKacoBlueplanetGridsave.WATCHDOG_TRIGGER_SECONDS - 1,
- ChronoUnit.SECONDS) //
- .output(WATCHDOG, null /* waiting till next watchdog trigger */)) //
+ .timeleap(CLOCK, WATCHDOG_TRIGGER_SECONDS - 1, SECONDS) //
+ .output(WATCHDOG.getChannelId(), null /* waiting till next watchdog trigger */)) //
.next(new TestCase() //
- .timeleap(clock, 1, ChronoUnit.SECONDS) //
- .output(WATCHDOG, BatteryInverterKacoBlueplanetGridsave.WATCHDOG_TIMEOUT_SECONDS)) //
+ .timeleap(CLOCK, 1, SECONDS) //
+ .output(WATCHDOG.getChannelId(), WATCHDOG_TIMEOUT_SECONDS)) //
;
}
}
diff --git a/io.openems.edge.batteryinverter.refu88k/.classpath b/io.openems.edge.batteryinverter.refu88k/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.batteryinverter.refu88k/.classpath
+++ b/io.openems.edge.batteryinverter.refu88k/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88k.java b/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88k.java
index 2a9e69ceb53..64a0473325d 100644
--- a/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88k.java
+++ b/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88k.java
@@ -43,16 +43,22 @@ public interface BatteryInverterRefuStore88k
*/
public static int RETRY_COMMAND_MAX_ATTEMPTS = 30;
+ /*
+ * Whenever one of these states would be Level.FAULT, the EssGeneric will stop
+ * the battery and the inverter. If this is necessary, it must be specifically
+ * mentioned and the state should have a proper description of the fault.
+ */
+
public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
STATE_MACHINE(Doc.of(State.values()) //
.text("Current State of State-Machine")), //
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
- MAX_START_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_START_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of start attempts failed")), //
- MAX_STOP_ATTEMPTS(Doc.of(Level.FAULT) //
+ MAX_STOP_ATTEMPTS(Doc.of(Level.WARNING) //
.text("The maximum number of stop attempts failed")), //
- INVERTER_CURRENT_STATE_FAULT(Doc.of(Level.FAULT) //
+ INVERTER_CURRENT_STATE_FAULT(Doc.of(Level.WARNING) //
.text("The 'CurrentState' is invalid")), //
/*
@@ -106,41 +112,41 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
ST(Doc.of(OperatingState.values())), //
ST_VND(Doc.of(VendorOperatingState.values())), //
// Evt1 Alarms and Warnings
- GROUND_FAULT(Doc.of(Level.FAULT) //
+ GROUND_FAULT(Doc.of(Level.WARNING) //
.text("Ground fault")), //
- DC_OVER_VOLTAGE(Doc.of(Level.FAULT) //
+ DC_OVER_VOLTAGE(Doc.of(Level.WARNING) //
.text("Dc over voltage")), //
- AC_DISCONNECT(Doc.of(Level.FAULT) //
+ AC_DISCONNECT(Doc.of(Level.WARNING) //
.text("AC disconnect open")), //
- DC_DISCONNECT(Doc.of(Level.FAULT) //
+ DC_DISCONNECT(Doc.of(Level.WARNING) //
.text("DC disconnect open")), //
- GRID_DISCONNECT(Doc.of(Level.FAULT) //
+ GRID_DISCONNECT(Doc.of(Level.WARNING) //
.text("Grid shutdown")), //
- CABINET_OPEN(Doc.of(Level.FAULT) //
+ CABINET_OPEN(Doc.of(Level.WARNING) //
.text("Cabinet open")), //
- MANUAL_SHUTDOWN(Doc.of(Level.FAULT) //
+ MANUAL_SHUTDOWN(Doc.of(Level.WARNING) //
.text("Manual shutdown")), //
- OVER_TEMP(Doc.of(Level.FAULT) //
+ OVER_TEMP(Doc.of(Level.WARNING) //
.text("Over temperature")), //
- OVER_FREQUENCY(Doc.of(Level.FAULT) //
+ OVER_FREQUENCY(Doc.of(Level.WARNING) //
.text("Frequency above limit")), //
- UNDER_FREQUENCY(Doc.of(Level.FAULT) //
+ UNDER_FREQUENCY(Doc.of(Level.WARNING) //
.text("Frequency under limit")), //
- AC_OVER_VOLT(Doc.of(Level.FAULT) //
+ AC_OVER_VOLT(Doc.of(Level.WARNING) //
.text("AC Voltage above limit")), //
- AC_UNDER_VOLT(Doc.of(Level.FAULT) //
+ AC_UNDER_VOLT(Doc.of(Level.WARNING) //
.text("AC Voltage under limit")), //
- BLOWN_STRING_FUSE(Doc.of(Level.FAULT) //
+ BLOWN_STRING_FUSE(Doc.of(Level.WARNING) //
.text("Blown String fuse on input")), //
- UNDER_TEMP(Doc.of(Level.FAULT) //
+ UNDER_TEMP(Doc.of(Level.WARNING) //
.text("Under temperature")), //
- MEMORY_LOSS(Doc.of(Level.FAULT) //
+ MEMORY_LOSS(Doc.of(Level.WARNING) //
.text("Generic Memory or Communication error (internal)")), //
- HW_TEST_FAILURE(Doc.of(Level.FAULT) //
+ HW_TEST_FAILURE(Doc.of(Level.WARNING) //
.text("Hardware test failure")), //
- OTHER_ALARM(Doc.of(Level.FAULT) //
+ OTHER_ALARM(Doc.of(Level.WARNING) //
.text("Other alarm")), //
- OTHER_WARNING(Doc.of(Level.FAULT) //
+ OTHER_WARNING(Doc.of(Level.WARNING) //
.text("Other warning")), //
EVT_2(Doc.of(OpenemsType.INTEGER).unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), //
EVT_VND_1(Doc.of(OpenemsType.INTEGER).unit(Unit.NONE).accessMode(AccessMode.READ_ONLY)), //
diff --git a/io.openems.edge.batteryinverter.refu88k/test/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImplTest.java b/io.openems.edge.batteryinverter.refu88k/test/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImplTest.java
index 08316f856cf..560aa3bb4f3 100644
--- a/io.openems.edge.batteryinverter.refu88k/test/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImplTest.java
+++ b/io.openems.edge.batteryinverter.refu88k/test/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImplTest.java
@@ -9,17 +9,14 @@
public class BatteryInverterRefuStore88kImplTest {
- private static final String BATTERY_INVERTER_ID = "batteryInverter0";
- private static final String MODBUS_ID = "modbus0";
-
@Test
public void test() throws Exception {
new ComponentTest(new BatteryInverterRefuStore88kImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_INVERTER_ID) //
- .setModbusId(MODBUS_ID) //
+ .setId("batteryInverter0") //
+ .setModbusId("modbus0") //
.setStartStop(StartStopConfig.AUTO) //
.setTimeLimitNoPower(0) //
.setWatchdoginterval(0) //
diff --git a/io.openems.edge.batteryinverter.sinexcel/.classpath b/io.openems.edge.batteryinverter.sinexcel/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.batteryinverter.sinexcel/.classpath
+++ b/io.openems.edge.batteryinverter.sinexcel/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcel.java b/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcel.java
index daa20ac5029..d54e8bbe2d1 100644
--- a/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcel.java
+++ b/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcel.java
@@ -44,7 +44,7 @@ public interface BatteryInverterSinexcel extends OffGridBatteryInverter, Managed
public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
STATE_MACHINE(Doc.of(State.values()) //
.text("Current State of State-Machine")), //
- RUN_FAILED(Doc.of(Level.FAULT) //
+ RUN_FAILED(Doc.of(Level.WARNING) //
.text("Running the Logic failed")), //
SET_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) //
.accessMode(AccessMode.READ_WRITE)//
@@ -96,7 +96,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
SERIAL_NUMBER(Doc.of(OpenemsType.STRING) //
.persistencePriority(PersistencePriority.HIGH) //
.accessMode(AccessMode.READ_ONLY)), //
- FAULT_STATUS(Doc.of(Level.FAULT) //
+ FAULT_STATUS(Doc.of(Level.WARNING) //
.accessMode(AccessMode.READ_ONLY)), //
ALERT_STATUS(Doc.of(Level.WARNING) //
.accessMode(AccessMode.READ_ONLY)), //
diff --git a/io.openems.edge.batteryinverter.sinexcel/test/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImplTest.java b/io.openems.edge.batteryinverter.sinexcel/test/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImplTest.java
index 6092ebde7d6..abf144c927e 100644
--- a/io.openems.edge.batteryinverter.sinexcel/test/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImplTest.java
+++ b/io.openems.edge.batteryinverter.sinexcel/test/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImplTest.java
@@ -1,5 +1,11 @@
package io.openems.edge.batteryinverter.sinexcel;
+import static io.openems.edge.batteryinverter.api.OffGridBatteryInverter.ChannelId.INVERTER_STATE;
+import static io.openems.edge.batteryinverter.api.SymmetricBatteryInverter.ChannelId.MAX_APPARENT_POWER;
+import static io.openems.edge.batteryinverter.sinexcel.BatteryInverterSinexcel.ChannelId.SET_OFF_GRID_MODE;
+import static io.openems.edge.batteryinverter.sinexcel.BatteryInverterSinexcel.ChannelId.SET_ON_GRID_MODE;
+import static io.openems.edge.batteryinverter.sinexcel.BatteryInverterSinexcel.ChannelId.STATE_MACHINE;
+
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
@@ -8,10 +14,8 @@
import io.openems.common.exceptions.OpenemsException;
import io.openems.common.test.TimeLeapClock;
-import io.openems.common.types.ChannelAddress;
import io.openems.edge.battery.api.Battery;
import io.openems.edge.battery.test.DummyBattery;
-import io.openems.edge.batteryinverter.api.OffGridBatteryInverter;
import io.openems.edge.batteryinverter.api.OffGridBatteryInverter.TargetGridMode;
import io.openems.edge.batteryinverter.sinexcel.enums.CountryCode;
import io.openems.edge.batteryinverter.sinexcel.enums.EnableDisable;
@@ -27,21 +31,9 @@
public class BatteryInverterSinexcelImplTest {
- private static final String BATTERY_INVERTER_ID = "batteryInverter0";
- private static final String BATTERY_ID = "battery0";
- private static final String MODBUS_ID = "modbus0";
- private static final ChannelAddress STATE_MACHINE = new ChannelAddress(BATTERY_INVERTER_ID, "StateMachine");
- private static final ChannelAddress SET_ON_GRID_MODE = new ChannelAddress(BATTERY_INVERTER_ID, "SetOnGridMode");
- private static final ChannelAddress SET_OFF_GRID_MODE = new ChannelAddress(BATTERY_INVERTER_ID, "SetOffGridMode");
- private static final ChannelAddress MAX_APPARENT_POWER = new ChannelAddress(BATTERY_INVERTER_ID, //
- "MaxApparentPower");
-
- private static final ChannelAddress INVERTER_STATE = new ChannelAddress(BATTERY_INVERTER_ID, //
- OffGridBatteryInverter.ChannelId.INVERTER_STATE.id());
-
private static class MyComponentTest extends ComponentTest {
- private final Battery battery = new DummyBattery(BATTERY_ID);
+ private final Battery battery = new DummyBattery("battery0");
public MyComponentTest(OpenemsComponent sut) throws OpenemsException {
super(sut);
@@ -64,11 +56,11 @@ public void testStart() throws Exception {
new MyComponentTest(new BatteryInverterSinexcelImpl()) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_INVERTER_ID) //
+ .setId("batteryInverter0") //
.setStartStopConfig(StartStopConfig.START) //
- .setModbusId(MODBUS_ID) //
+ .setModbusId("modbus0") //
.setCountryCode(CountryCode.GERMANY)//
.setEmergencyPower(EnableDisable.DISABLE)//
.build()) //
@@ -100,11 +92,11 @@ public void testOffGrid() throws Exception {
new MyComponentTest(sut) //
.addReference("cm", new DummyConfigurationAdmin()) //
.addReference("componentManager", new DummyComponentManager(clock)) //
- .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
.activate(MyConfig.create() //
- .setId(BATTERY_INVERTER_ID) //
+ .setId("batteryInverter0") //
.setStartStopConfig(StartStopConfig.START) //
- .setModbusId(MODBUS_ID) //
+ .setModbusId("modbus0") //
.setCountryCode(CountryCode.GERMANY)//
.setEmergencyPower(EnableDisable.DISABLE)//
.build()) //
diff --git a/io.openems.edge.batteryinverter.sunspec/.classpath b/io.openems.edge.batteryinverter.sunspec/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.batteryinverter.sunspec/.classpath
+++ b/io.openems.edge.batteryinverter.sunspec/.classpath
@@ -1,7 +1,7 @@
-
+
diff --git a/io.openems.edge.bosch.bpts5hybrid/.classpath b/io.openems.edge.bosch.bpts5hybrid/.classpath
index bbfbdbe40e7..b4cffd0fe60 100644
--- a/io.openems.edge.bosch.bpts5hybrid/.classpath
+++ b/io.openems.edge.bosch.bpts5hybrid/.classpath
@@ -1,7 +1,7 @@
-
+