Skip to content

Commit

Permalink
FEMS Backports (#2446)
Browse files Browse the repository at this point in the history
* AppCenter: fix not automatically installing missing dependencies

user is null while resolving missing dependencies which resulted in a NullPointerException

```
2023-11-15T12:13:02,237 [orker-10] ERROR [appmanager.ResolveDependencies] Could not resolve dependencies!
java.lang.NullPointerException: Cannot invoke "io.openems.edge.common.user.User.getLanguage()" because "<parameter1>" is null
	at io.openems.edge.core.appmanager.AppManagerImpl.lambda$15(AppManagerImpl.java:517) ~[?:?]
	at io.openems.edge.core.appmanager.AppManagerImpl.lockModifyingApps(AppManagerImpl.java:892) ~[?:?]
	at io.openems.edge.core.appmanager.AppManagerImpl.handleAddAppInstanceRequest(AppManagerImpl.java:515) ~[?:?]
	at io.openems.edge.core.appmanager.ResolveDependencies.resolveDependencies(ResolveDependencies.java:108) ~[?:?]
	at io.openems.edge.core.appmanager.ResolveDependencies.run(ResolveDependencies.java:42) ~[?:?]
	at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1395) ~[?:?]
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?]
	at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?]
	at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?]
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?]
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?]
```
Everything still works there will only be AppCenter info and debugLog of a missing dependency if there are missing dependencies

* AppCenter: Integrated systems

* Edge: Check Battery Combination only if the battery is started

Check the combination of Battery and GoodWe only if the battery was started.
Set default values as long as the goodwe is not started.

* UI Time-Of-Use: refactor chart in Live modal

* Time-of-Use Controller: improvements for BETA test

- Add Penalize/Constraints for bad solutions
- Never apply charge for full batteries in run()
- Use ConsumptionPower for short term prediction instead of UnmanagedConsumptionPower
- Error in STROMDAO App on fems51290: validating ZIP_CODE
- Handle trailing non-available values for production/consumption/price
- Values of "StateMachine"-Channels are still sometimes decimals. Handle aggregation in Edge
- round enums to their most appearing value not average
- fix tibber app

* UI: Extending Systemexecute

- adding option to build and run debian-package (from a branch) on a fems
- changing field for custom commands from input to text-area

* UI: refactor channelthreshold history

Example for all relay data dependent charts -> active time over period

* added time to Chart types

* AppCenter: Home 20/30 available relays

Edge:
 - add possibility to filter components via ServiceUtils
 - moved HeatProps to common RelayProps
 - added Filter options to relays
 - changed cardinality of HeatPump, HeatingElement, CombinedHeatAndPower to MULTIPLE
 - updated ManualRelayControl, ThresholdControl to use Props
 - removed unnecessary relay helper methods and added/update some
 - fix checking if a relay is used when set inside an array
 - added option for MultiSelect of SelectGroup
 - added possibility for custom filters in CheckRelayCount
 - updated translations
 - added test for default relay ports for Home 10 & 30
 - added more apps to translation tests
 - added possibility to defined number of ios and io prefix for dummy input output

UI:
 - add possibility to set the option group picker to multi

* GoodWe Battery-Inverter: improve property descriptions

Rename the property name and description of "Enable Feed In To Grid", as the name and description are misleading.

* UI: fix format number not using configuration

* Charts: Using the configured format instead of an empty string

* TypeUtils: update coding style + handle NaN/Infinite

* UI: add check in prepareBatteryExtension for isRunning

* GoodWe: implement RippleControlReceiver; update config descriptions

This PR also includes
- general methods to set a channel write value until the read value is the same.
- improve titles/descriptions of GoodWe Battery-Inverter

* Time-of-Use Controller: improvements for BETA test

- Increase max calculation time to 11 minutes
- Use existingSchedule as basis for initial population
- Repair/Postprocess bad solutions

* GoodWe: revert SetWriteValueIfNotRead for ShadowEnable

* AppCenter: Home NoFeedInLimitation & correct RCR config

* Fix JUnit test

* Quick fix for Simulated EVCS

See https://community.openems.io/t/errors-in-simulating-evcs/2014/3

---------

Co-authored-by: Michael Grill <[email protected]>
Co-authored-by: Sebastian Asen <[email protected]>
Co-authored-by: Sagar Venu <[email protected]>
Co-authored-by: Stefan Feilmeier <[email protected]>
Co-authored-by: Lorant Meszlenyi <[email protected]>
Co-authored-by: Lukas Rieger <[email protected]>
  • Loading branch information
7 people authored Nov 28, 2023
1 parent 887f076 commit d72cdab
Show file tree
Hide file tree
Showing 129 changed files with 5,958 additions and 2,354 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package io.openems.edge.common.channel;
package io.openems.common.test;

import io.openems.common.types.OptionsEnum;

public enum TestOptions implements OptionsEnum {
public enum DummyOptionsEnum implements OptionsEnum {
UNDEFINED(-1, "Undefined"), //
OPTION_1(1, "Option 1"), //
OPTION_2(2, "Option 2"); //
VALUE_1(1, "One"), //
;

private final int value;
private final String name;

private TestOptions(int value, String name) {
private DummyOptionsEnum(int value, String name) {
this.value = value;
this.name = name;
}
Expand Down
44 changes: 41 additions & 3 deletions io.openems.common/src/io/openems/common/utils/ServiceUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Objects;

import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;

public class ServiceUtils {
Expand All @@ -13,9 +14,11 @@ public static class CloseableService<T> implements AutoCloseable {
private final ServiceReference<T> serviceReference;
private final T service;

public CloseableService(BundleContext bundleContext, Class<T> clazz) {
public CloseableService(BundleContext bundleContext, Class<T> clazz, String filter)
throws InvalidSyntaxException {
this.bundleContext = Objects.requireNonNull(bundleContext);
this.serviceReference = bundleContext.getServiceReference(Objects.requireNonNull(clazz));
final var foundServices = bundleContext.getServiceReferences(Objects.requireNonNull(clazz), filter);
this.serviceReference = foundServices.size() != 0 ? foundServices.iterator().next() : null;
if (this.serviceReference == null) {
this.service = null;
return;
Expand Down Expand Up @@ -63,7 +66,42 @@ public static <T> CloseableService<T> useService(//
final BundleContext bundleContext, //
final Class<T> clazz //
) {
return new CloseableService<>(bundleContext, clazz);
try {
return useService(bundleContext, clazz, null);
} catch (InvalidSyntaxException e) {
// exception can only be thrown with filter
throw new RuntimeException(e);
}
}

/**
* Creates a {@link CloseableService} with the given parameters.
*
* <p>
* Usage: <br>
*
* <pre>
* try (var componentManagerService = ServiceUtils.useService(bundleContext, ComponentManager.class)) {
* var componentManager = componentManagerService.getService();
* // use componentManager here (may be null) ...
* } catch (Exception e) {
* }
* </pre>
*
* @param <T> the type of the service
* @param bundleContext the {@link BundleContext} of the service
* @param clazz the class type of the service
* @param filter the filter expression or null for any service
* @return the {@link CloseableService}
* @throws InvalidSyntaxException If the specified filter contains an invalid
* filter expression that cannot be parsed.
*/
public static <T> CloseableService<T> useService(//
final BundleContext bundleContext, //
final Class<T> clazz, //
final String filter // nullable
) throws InvalidSyntaxException {
return new CloseableService<>(bundleContext, clazz, filter);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;

import com.google.common.base.CaseFormat;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;
import io.openems.common.types.OptionsEnum;
import io.openems.edge.common.channel.value.Value;
import io.openems.edge.common.component.OpenemsComponent;

Expand Down Expand Up @@ -109,4 +112,63 @@ private static <T extends Record> T getValues(OpenemsComponent component, Class<
}
}
}

/**
* Set write value of a {@link EnumWriteChannel} if the read value is not equal.
*
* <p>
* Use this method if you do not want to write a Channel on every cycle, but
* only if the Write-Values differs from the current Read-Value.
*
* @param channel the {@link EnumWriteChannel}
* @param value value to be set
* @throws OpenemsNamedException on error
*/
public static void setWriteValueIfNotRead(EnumWriteChannel channel, OptionsEnum value)
throws OpenemsNamedException {
if (Objects.equals(channel.value().get(), value.getValue())) {
return;
}
channel.setNextWriteValue(value);
}

/**
* Set write value of a {@link IntegerWriteChannel} if the read value is not
* equal.
*
* <p>
* Use this method if you do not want to write a Channel on every cycle, but
* only if the Write-Values differs from the current Read-Value.
*
* @param channel the {@link IntegerWriteChannel}
* @param value value to be set
* @throws OpenemsNamedException on error
*/
public static void setWriteValueIfNotRead(IntegerWriteChannel channel, Integer value) throws OpenemsNamedException {
setWriteValueIfNotReadHelper(channel, value);
}

/**
* Set write value of a {@link BooleanWriteChannel} if the read value is not
* equal.
*
* <p>
* Use this method if you do not want to write a Channel on every cycle, but
* only if the Write-Values differs from the current Read-Value.
*
* @param channel the {@link BooleanWriteChannel}
* @param value value to be set
* @throws OpenemsNamedException on error
*/
public static void setWriteValueIfNotRead(BooleanWriteChannel channel, Boolean value) throws OpenemsNamedException {
setWriteValueIfNotReadHelper(channel, value);
}

private static <T> void setWriteValueIfNotReadHelper(WriteChannel<T> channel, T value)
throws OpenemsNamedException {
if (Objects.equals(channel.value().get(), value)) {
return;
}
channel.setNextWriteValue(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,20 @@ public static void activateNextProcessImage(OpenemsComponent component) {
* @param value the new value
*/
public static void withValue(OpenemsComponent component, ChannelId channelId, Object value) {
var channel = component.channel(channelId);
withValue(component.channel(channelId), value);
}

/**
* Sets the value on a Channel and activates the Process Image.
*
* <p>
* This is useful to simulate a Channel value in a Unit test, as the value
* becomes directly available on the Channel.
*
* @param channel the {@link Channel}
* @param value the new value
*/
public static void withValue(Channel<?> channel, Object value) {
channel.setNextValue(value);
channel.nextProcessImage();
}
Expand Down
Loading

0 comments on commit d72cdab

Please sign in to comment.