Skip to content

Commit

Permalink
Backport from FEMS for 2022.06 (OpenEMS#1850)
Browse files Browse the repository at this point in the history
- Common:
  - Add type casting to JsonUtils
  - WebSocket reconnect: small fixes
- Backend:
  - Updates to Alerting Service
  - EdgeWebsocket: trim apikey
- Edge:
  - Battery:
    - Add FENECON Commercial Battery
    - Soltaro Cluster C: fix possible race condition on init
  - Battery Inverter:
    - Sinexcel: large improvements
  - Controller:
    - Api.Backend: fix data send on duplicated Component-ID
    - ESS GridOptimizedCharge: large improvements
  - Timedata:
    - RRD4j: better handling for missing database files
    - Influx: enable GZIP and improve logging
  - Edge.Common:
    - Refactor ComponentManager
    - Add Yuriy Guskovs Plot library for tests
  • Loading branch information
sfeilmeier authored Jun 1, 2022
1 parent 4aec48a commit e17b229
Show file tree
Hide file tree
Showing 64 changed files with 5,599 additions and 495 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class Alerting extends AbstractOpenemsBackendComponent implements EventHa
protected EventAdmin eventAdmin;

@Reference
protected Mailer notifier;
protected Mailer mailer;

public Alerting() {
super("Alerting");
Expand Down Expand Up @@ -129,47 +129,56 @@ private void tryAddEdge(Edge edge) {
}

/**
* send e-mail via notifier service.
* send e-mail via mailer service.
*
* @param stamp at with mail send was initialized
* @param user list of recipents
* @param user list of recipients
*/
private void sendEmails(ZonedDateTime stamp, List<EdgeUser> user) {
// log to Console
this.logInfo(this.log, "send Email - to " + user.size() + " user");
this.notifier.sendAlertingMail(stamp, user);
this.mailer.sendAlertingMail(stamp, user);
}

/**
* get TimeStamp of next notification or null if no notification is needed.
*
* @param edge thats involved
* @param user that will recieve the mail
* @param user that will receive the mail
* @return Optional of ZonedDateTime
*/
private Optional<ZonedDateTime> getNotifyStamp(Edge edge, EdgeUser user) {
ZonedDateTime lastStamp = user.getLastNotification(ZoneId.systemDefault());
ZonedDateTime lastOnline = edge.getLastMessageTimestamp();
int timeToWait = user.getTimeToWait();

ZonedDateTime notifyStamp = null;
if (lastOnline == null) {
this.logDebug(this.log, "[" + edge.getId() + "] has no TimeStamp");
// timeToWait <= 0 equals OFF
if (timeToWait <= 0) {
return Optional.ofNullable(null);
} else {
int timeToWait = user.getTimeToWait();

// tmeToWait <= 0 equals OFF
if (timeToWait > 0) {
notifyStamp = lastOnline.withZoneSameInstant(ZoneId.systemDefault()) //
ZonedDateTime lastOnline = edge.getLastMessageTimestamp();
if (lastOnline == null) {
// If the System was never Online
this.logDebug(this.log, "[" + edge.getId() + "] has no TimeStamp");
return Optional.ofNullable(null);
} else {
// Last TimeStamp at which the Edge was Online
ZonedDateTime lastStamp = user.getLastNotification(ZoneId.systemDefault());
// The TimeStamp at which to send the notification
ZonedDateTime notifyStamp = lastOnline.withZoneSameInstant(ZoneId.systemDefault()) //
.plus(timeToWait, ChronoUnit.MINUTES);

// If Notification TimeStamp is before lastOnline => Mail was already sent
if (lastStamp != null && !notifyStamp.isAfter(lastStamp)) {
notifyStamp = null;
}
return Optional.ofNullable(notifyStamp);
}
}
return Optional.ofNullable(notifyStamp);
}

/**
* Handler for when the Edge.OnSetOnline Event was thrown.
*
* @param reader Reader for Event parameters
*/
private void handleEdgeOnSetOnline(EventReader reader) {
boolean isOnline = reader.getBoolean(Edge.Events.OnSetOnline.IS_ONLINE);
Edge edge = reader.getProperty(Edge.Events.OnSetOnline.EDGE);
Expand All @@ -181,6 +190,11 @@ private void handleEdgeOnSetOnline(EventReader reader) {
}
}

/**
* Hander for when the Metadata.AfterInitialize Event was thrown.
*
* @param reader EventReader for parameters
*/
private void handleMetadataAfterInitialize(EventReader reader) {
Executors.newSingleThreadScheduledExecutor().schedule(() -> {
this.checkMetadata();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void run(WebSocket ws, JsonObject handshake) {
if (!apikeyOpt.isPresent()) {
throw new OpenemsException("Apikey is missing in handshake");
}
apikey = apikeyOpt.get();
apikey = apikeyOpt.get().trim();
wsData.setApikey(apikey);

// get edgeId for apikey
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.openems.common.function;

/**
* This interface is similar to the java.util interface
* {@link ThrowingBiFunction}. Difference is, that it allows to pass to the
* apply() method one more parameter.
*
* @param <T> the apply methods first argument type
* @param <U> the apply methods second argument type
* @param <S> the apply methods third argument type
* @param <R> the type of the result of the function
* @param <E> the exception type
*/
@FunctionalInterface
public interface ThrowingTriFunction<T, U, S, R, E extends Exception> {

/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @param s the third function argument
* @return the function result
* @throws E on error
*/
public R apply(T t, U u, S s) throws E;

}
19 changes: 10 additions & 9 deletions io.openems.common/src/io/openems/common/utils/JsonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1350,27 +1350,28 @@ public static Object getAsType(Class<?> type, JsonElement j) throws NotImplement
* @param j the {@link JsonElement}
* @return an Object of the given type
*/
public static Object getAsType(OpenemsType type, JsonElement j) throws OpenemsNamedException {
@SuppressWarnings("unchecked")
public static <T> T getAsType(OpenemsType type, JsonElement j) throws OpenemsNamedException {
if (j.isJsonNull()) {
return null;
}

if (j.isJsonPrimitive()) {
switch (type) {
case BOOLEAN:
return JsonUtils.getAsBoolean(j);
return (T) Boolean.valueOf(JsonUtils.getAsBoolean(j));
case DOUBLE:
return JsonUtils.getAsDouble(j);
return (T) Double.valueOf(JsonUtils.getAsDouble(j));
case FLOAT:
return JsonUtils.getAsFloat(j);
return (T) Float.valueOf(JsonUtils.getAsFloat(j));
case INTEGER:
return JsonUtils.getAsInt(j);
return (T) Integer.valueOf(JsonUtils.getAsInt(j));
case LONG:
return JsonUtils.getAsLong(j);
return (T) Long.valueOf(JsonUtils.getAsLong(j));
case SHORT:
return JsonUtils.getAsShort(j);
return (T) Short.valueOf(JsonUtils.getAsShort(j));
case STRING:
return JsonUtils.getAsString(j);
return (T) JsonUtils.getAsString(j);
}
}

Expand All @@ -1384,7 +1385,7 @@ public static Object getAsType(OpenemsType type, JsonElement j) throws OpenemsNa
case SHORT:
break;
case STRING:
return j.toString();
return (T) j.toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

public class ClientReconnectorWorker extends AbstractWorker {

private static final int MAX_WAIT_SECONDS = 120;
private static final int MAX_WAIT_SECONDS = 100;
private static final int MIN_WAIT_SECONDS = 10;

private static final long MIN_WAIT_SEONDCS_BETWEEN_RETRIES = new Random()
private static final long MIN_WAIT_SECONDS_BETWEEN_RETRIES = new Random()
.nextInt(ClientReconnectorWorker.MAX_WAIT_SECONDS) + ClientReconnectorWorker.MIN_WAIT_SECONDS;

private final Logger log = LoggerFactory.getLogger(ClientReconnectorWorker.class);
Expand All @@ -41,15 +41,17 @@ protected void forever() throws InterruptedException {
}

var waitedSeconds = Duration.between(this.lastTry, now).getSeconds();
if (waitedSeconds < ClientReconnectorWorker.MIN_WAIT_SEONDCS_BETWEEN_RETRIES) {
if (waitedSeconds < ClientReconnectorWorker.MIN_WAIT_SECONDS_BETWEEN_RETRIES) {
this.parent.logInfo(this.log, "Waiting till next WebSocket reconnect ["
+ (ClientReconnectorWorker.MIN_WAIT_SEONDCS_BETWEEN_RETRIES - waitedSeconds) + "s]");
+ (ClientReconnectorWorker.MIN_WAIT_SECONDS_BETWEEN_RETRIES - waitedSeconds) + "s]");
return;
}
this.lastTry = now;

this.parent.logInfo(this.log, "Reconnecting WebSocket...");
ws.reconnectBlocking();
this.parent.logInfo(this.log,
"Reconnected WebSocket successfully [" + Duration.between(now, Instant.now()).toSeconds() + "s]");
}

@Override
Expand Down
2 changes: 2 additions & 0 deletions io.openems.edge.application/EdgeApp.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
bnd.identity;id='io.openems.edge.application',\
bnd.identity;id='io.openems.edge.battery.bmw',\
bnd.identity;id='io.openems.edge.battery.bydcommercial',\
bnd.identity;id='io.openems.edge.battery.fenecon.commercial',\
bnd.identity;id='io.openems.edge.battery.fenecon.home',\
bnd.identity;id='io.openems.edge.battery.soltaro',\
bnd.identity;id='io.openems.edge.batteryinverter.kaco.blueplanetgridsave',\
Expand Down Expand Up @@ -170,6 +171,7 @@
io.openems.edge.battery.api;version=snapshot,\
io.openems.edge.battery.bmw;version=snapshot,\
io.openems.edge.battery.bydcommercial;version=snapshot,\
io.openems.edge.battery.fenecon.commercial;version=snapshot,\
io.openems.edge.battery.fenecon.home;version=snapshot,\
io.openems.edge.battery.soltaro;version=snapshot,\
io.openems.edge.batteryinverter.api;version=snapshot,\
Expand Down
12 changes: 12 additions & 0 deletions io.openems.edge.battery.fenecon.commercial/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="src" output="bin_test" path="test">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
2 changes: 2 additions & 0 deletions io.openems.edge.battery.fenecon.commercial/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin_test/
/generated/
23 changes: 23 additions & 0 deletions io.openems.edge.battery.fenecon.commercial/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>io.openems.edge.battery.fenecon.commercial</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>bndtools.core.bndbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>bndtools.core.bndnature</nature>
</natures>
</projectDescription>
17 changes: 17 additions & 0 deletions io.openems.edge.battery.fenecon.commercial/bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Bundle-Name: OpenEMS Edge Battery FENECON Commercial
Bundle-Vendor: FENECON GmbH
Bundle-License: https://opensource.org/licenses/EPL-2.0
Bundle-Version: 1.0.0.${tstamp}

-buildpath: \
${buildpath},\
io.openems.common,\
io.openems.edge.battery.api,\
io.openems.edge.bridge.modbus,\
io.openems.edge.common,\
io.openems.edge.ess.api,\
io.openems.edge.io.api,\

-testpath: \
${testpath},\
com.ghgande.j2mod
3 changes: 3 additions & 0 deletions io.openems.edge.battery.fenecon.commercial/readme.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= Battery FENECON Commercial

https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.battery.fenecon.commercial[Source Code icon:github[]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.openems.edge.battery.fenecon.commercial;

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 100; // [A]
}

@Override
public int getInitialBmsMaxEverDischargeCurrent() {
return 100; // [A]
}

// Over voltage Protection
@Override
public PolyLine getChargeVoltageToPercent() {
return PolyLine.create() //
.addPoint(3000, 0.1) //
.addPoint(Math.nextUp(3000), 1) //
.addPoint(3485, 1) //
.addPoint(3490, 0.9) //
.addPoint(3570, 0.01) //
.addPoint(3600, 0.01) //
.addPoint(Math.nextDown(3600), 0) //
.addPoint(3600, 0) //
.build();
}

// Low Voltage protection
@Override
public PolyLine getDischargeVoltageToPercent() {
return PolyLine.create() //
.addPoint(3000, 0) //
.addPoint(Math.nextUp(3000), 0.1) //
.addPoint(3030, 0.02) //
.addPoint(3050, 0.02) //
.addPoint(3140, 1) //
.addPoint(3600, 1) //
.addPoint(Math.nextUp(3600), 1) //
.build();
}

@Override
public PolyLine getChargeTemperatureToPercent() {
return PolyLine.create() //
.addPoint(0, 0) //
.addPoint(Math.nextUp(0), 0.01) //
.addPoint(15, 1) //
.addPoint(50, 1) //
.addPoint(Math.nextDown(54), 0.01) //
.addPoint(55, 0) //
.build();
}

@Override
public PolyLine getDischargeTemperatureToPercent() {
return PolyLine.create() //
.addPoint(0, 0) //
.addPoint(Math.nextUp(0), 0.01) //
.addPoint(10, 1) //
.addPoint(50, 1) //
.addPoint(Math.nextDown(54), 0.01) //
.addPoint(55, 0) //
.build();
}

@Override
public ForceDischarge.Params getForceDischargeParams() {
return new ForceDischarge.Params(3700, 3630, 3610);
}

@Override
public ForceCharge.Params getForceChargeParams() {
return new ForceCharge.Params(2850, 2950, 3030);
}

@Override
public Double getMaxIncreaseAmperePerSecond() {
return 0.5; // [A] per second
}

}
Loading

0 comments on commit e17b229

Please sign in to comment.