Skip to content

Commit

Permalink
Improve handling of OSGi References and target filters (#1603)
Browse files Browse the repository at this point in the history
* Try to restart Components if they are unavailable

Due to a race condition on startup, sometimes Components are unavailable to the ComponentManager. The related error message is `[Core.ComponentManager(1)] : Could not get service from ref`. We are fixing this by restarting (i.e. 'reconfiguring') the Component in such a case.

* Increase ComponentManager 'ConfigNotActivated' level to 'FAULT'

* On UpdateComponent via ComponentManager: reset target properties

Before if a Component was referencing 'modbus0' and you would update it to 'modbus1', there were two possible behaviours. (1) if 'modbus0' was still existing, the update would work and the target filter would be set properly. (2) otherwise the target filter could not be set, because the Component would not reactivate. This would leave the Component in an error state. We are fixing this by always resetting the target properties.

* Set non-empty default target filter by default

If the target filter is made available via 'Config.java' and the configuration property is left as an empty string, the error `Invalid syntax in target property for dependency` arises on activate, which showed to also cause wrong behaviour later. We fix this by setting a default target filter of `(enabled=true)` - which is always true, but avoids the error

Thanks @sebastianasen for your valuable inputs.
  • Loading branch information
sfeilmeier committed Aug 26, 2021
1 parent df89482 commit 24b7e60
Show file tree
Hide file tree
Showing 84 changed files with 131 additions and 118 deletions.
Expand Up @@ -24,8 +24,8 @@
int modbusUnitId() default 1;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "$projectName$ [{id}]";

}
Expand Up @@ -32,7 +32,7 @@
long errorDelay() default 600;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
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;
Expand Down
Expand Up @@ -64,7 +64,7 @@
* @return String
*/
@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

/**
* The webconsole_configurationFactory_nameHint.
Expand Down
Expand Up @@ -29,7 +29,7 @@
int modbusUnitId() default 1;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Battery FENECON Home [{id}]";

Expand Down
Expand Up @@ -57,7 +57,7 @@
int minimalCellVoltage() default 2800;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "BMS Soltaro Cluster Version B [{id}]";
}
Expand Up @@ -114,7 +114,7 @@
* @return String
*/
@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

/**
* Gets the webconsole_configurationFactory_nameHint.
Expand Down
Expand Up @@ -50,7 +50,7 @@
BatteryState batteryState() default BatteryState.DEFAULT;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "BMS Soltaro Single Rack Version A [{id}]";
}
Expand Up @@ -145,7 +145,7 @@
* @return Modbus_target
*/
@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

/**
* Return the webconsole_configurationFactory_nameHint.
Expand Down
Expand Up @@ -114,7 +114,7 @@
* @return Modbus_target
*/
@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

/**
* Return the webconsole_configurationFactory_nameHint.
Expand Down
Expand Up @@ -29,7 +29,7 @@
String modbus_id() default "modbus0";

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Battery-Inverter KACO blueplanet gridsave [{id}]";

Expand Down
Expand Up @@ -32,7 +32,7 @@
StartStopConfig startStop() default StartStopConfig.AUTO;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

@AttributeDefinition(name = "Watchdog", description = "Sets the watchdog timer interval in seconds, 0=disable")
int watchdoginterval() default 0;
Expand Down
Expand Up @@ -26,7 +26,7 @@
int readFromCommonBlockNo() default 1;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Battery-Inverter SunSpec [{id}]";

Expand Down
Expand Up @@ -21,7 +21,7 @@
public interface ComponentManager extends OpenemsComponent, JsonApi, ClockProvider {

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
CONFIG_NOT_ACTIVATED(Doc.of(Level.WARNING) //
CONFIG_NOT_ACTIVATED(Doc.of(Level.FAULT) //
.text("A configured OpenEMS Component was not activated")), //
DUPLICATED_COMPONENT_ID(Doc.of(Level.FAULT) //
.text("Configuration has duplicated Component-IDs")), //
Expand Down
Expand Up @@ -29,7 +29,7 @@
int maxConcurrentConnections() default AbstractModbusTcpApi.DEFAULT_MAX_CONCURRENT_CONNECTIONS;

@AttributeDefinition(name = "Components target filter", description = "This is auto-generated by 'Component-IDs'.")
String Component_target() default "";
String Component_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Api Modbus/TCP Read-Only [{id}]";
}
Expand Up @@ -32,7 +32,7 @@
int maxConcurrentConnections() default AbstractModbusTcpApi.DEFAULT_MAX_CONCURRENT_CONNECTIONS;

@AttributeDefinition(name = "Components target filter", description = "This is auto-generated by 'Component-IDs'.")
String Component_target() default "";
String Component_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Api Modbus/TCP Read-Write [{id}]";
}
Expand Up @@ -33,10 +33,10 @@
int waitForHysteresis() default 20;

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

@AttributeDefinition(name = "Meter target filter", description = "This is auto-generated by 'Meter-ID'.")
String meter_target() default "";
String meter_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Ess Voltage Active Power Characteristic [{id}]";
}
Expand Up @@ -27,7 +27,7 @@
int power();

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Ess Fix Active Power [{id}]";
}
Expand Up @@ -46,10 +46,10 @@
int sellToGridLimitRampPercentage() default 2;

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

@AttributeDefinition(name = "Meter target filter", description = "This is auto-generated by 'Grid-Meter-ID'.")
String meter_target() default "";
String meter_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Ess Grid Optimized Charge [{id}]";
}
Expand Up @@ -21,7 +21,7 @@
String ess_id() default "ess0";

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Ess Hybrid Surplus-Feed-To-Grid [{id}]";

Expand Down
Expand Up @@ -33,7 +33,7 @@
StartDirection startDirection() default StartDirection.CHARGE;

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Ess Linear Power Band [{id}]";
}
Expand Up @@ -33,10 +33,10 @@
int waitForHysteresis() default 20;

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

@AttributeDefinition(name = "Meter target filter", description = "This is auto-generated by 'Meter-ID'.")
String meter_target() default "";
String meter_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Ess Voltage Reactive Power Characteristic [{id}]";

Expand Down
Expand Up @@ -27,10 +27,10 @@
int maximumSellToGridPower();

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

@AttributeDefinition(name = "Meter target filter", description = "This is auto-generated by 'Grid-Meter-ID'.")
String meter_target() default "";
String meter_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Ess Sell-to-Grid Limit [{id}]";
}
Expand Up @@ -45,7 +45,7 @@
int energySessionLimit() default 0;

@AttributeDefinition(name = "Evcs target filter", description = "This is auto-generated by 'Evcs-ID'.")
String evcs_target() default "";
String evcs_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Electric Vehicle Charging Station [{id}]";

Expand Down
Expand Up @@ -27,10 +27,10 @@
String schedule() default "";

@AttributeDefinition(name = "Ess target filter", description = "This is auto-generated by 'Ess-ID'.")
String ess_target() default "";
String ess_target() default "(enabled=true)";

@AttributeDefinition(name = "Grid-Meter target filter", description = "This is auto-generated by 'Grid-Meter-ID'.")
String meter_target() default "";
String meter_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "Controller Balancing Schedule Symmetric [{id}]";
}
Expand Up @@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -323,6 +324,15 @@ protected CompletableFuture<JsonrpcResponseSuccess> handleUpdateComponentConfigR
throw OpenemsError.EDGE_UNABLE_TO_APPLY_CONFIG.exception(request.getComponentId(),
config.getPid() + ": Properties is 'null'");
}

// Reset all target properties to avoid missing old references
for (Enumeration<String> k = properties.keys(); k.hasMoreElements();) {
String property = k.nextElement();
if (property.endsWith(".target")) {
properties.put(property, "(enabled=true)");
}
}

for (Property property : request.getProperties()) {
// do not allow certain properties to be updated, like pid and service.pid
if (!EdgeConfig.ignorePropertyKey(property.getName())) {
Expand Down
@@ -1,12 +1,12 @@
package io.openems.edge.core.componentmanager;

import java.io.IOException;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -59,11 +59,6 @@ public class OsgiValidateWorker extends ComponentManagerWorker {
*/
private final Map<String, String> defectiveComponents = new HashMap<>();

/**
* Delays announcement of defective Components by one execution Cycle.
*/
private final Set<String> lastDefectiveComponents = new HashSet<>();

/**
* Components with duplicated Component-IDs.
*/
Expand Down Expand Up @@ -94,18 +89,12 @@ private void findDefectiveComponents() {
final Configuration[] configs = this.readEnabledConfigurations();
final Map<String, String> defectiveComponents = new HashMap<>();
updateInactiveComponentsUsingScr(defectiveComponents, this.parent.serviceComponentRuntime);
updateInactiveComponentsUsingConfigurationAdmin(defectiveComponents, this.parent.getEnabledComponents(),
configs);
this.updateInactiveComponentsUsingConfigurationAdmin(defectiveComponents, this.parent.getEnabledComponents(),
configs, this.parent.serviceComponentRuntime);
this.parent._setConfigNotActivated(!defectiveComponents.isEmpty());
synchronized (this.defectiveComponents) {
this.defectiveComponents.clear();
for (Entry<String, String> c : defectiveComponents.entrySet()) {
if (this.lastDefectiveComponents.contains(c.getKey())) {
// Delay announcement of defective Components by one execution Cycle.
this.defectiveComponents.put(c.getKey(), c.getValue());
}
}
this.lastDefectiveComponents.addAll(defectiveComponents.keySet());
this.defectiveComponents.putAll(defectiveComponents);
}
}

Expand Down Expand Up @@ -171,8 +160,8 @@ private static void updateInactiveComponentsUsingScr(Map<String, String> defecti
*
* @param configs enabled {@link Configuration}s from {@link ConfigurationAdmin}
*/
private static void updateInactiveComponentsUsingConfigurationAdmin(Map<String, String> defectiveComponents,
List<OpenemsComponent> enabledComponents, Configuration[] configs) {
private void updateInactiveComponentsUsingConfigurationAdmin(Map<String, String> defectiveComponents,
List<OpenemsComponent> enabledComponents, Configuration[] configs, ServiceComponentRuntime scr) {
for (Configuration config : configs) {
Dictionary<String, Object> properties;
try {
Expand All @@ -192,7 +181,21 @@ private static void updateInactiveComponentsUsingConfigurationAdmin(Map<String,
continue;
}
if (!isComponentActivated(enabledComponents, componentId)) {
defectiveComponents.putIfAbsent(componentId, "Missing Bundle");
String factoryPid = config.getFactoryPid();
if (factoryPid != null && scr.getComponentDescriptionDTOs().stream()
.anyMatch(description -> factoryPid.equals(description.name))) {
// Bundle exists -> try to restart Component
try {
this.parent.logInfo(this.log, "Trying to restart Component [" + componentId + "]");
config.update(properties);
} catch (IOException e) {
e.printStackTrace();
defectiveComponents.putIfAbsent(componentId, "Unable to restart: " + e.getMessage());
}
} else {
// Bundle with this name does not exist
defectiveComponents.putIfAbsent(componentId, "Missing Bundle");
}
}
}
}
Expand Down
Expand Up @@ -27,7 +27,7 @@
int capacity() default 47000;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "ESS ADS-TEC StoraXe [{id}]";
}
Expand Up @@ -30,13 +30,13 @@
String modbus_id2();

@AttributeDefinition(name = "Modbus0 target filter", description = "This is auto-generated by 'Modbus-ID 0'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

@AttributeDefinition(name = "Modbus1 target filter", description = "This is auto-generated by 'Modbus-ID 1'.")
String modbus1_target() default "";
String modbus1_target() default "(enabled=true)";

@AttributeDefinition(name = "Modbus2 target filter", description = "This is auto-generated by 'Modbus-ID 2'.")
String modbus2_target() default "";
String modbus2_target() default "(enabled=true)";

String webconsole_configurationFactory_nameHint() default "ESS FENECON BYD Container [{id}]";
}
Expand Up @@ -27,7 +27,7 @@
int powerLimitOnPowerDecreaseCausedByOvertemperatureChannel() default 20_000;

@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
String Modbus_target() default "";
String Modbus_target() default "(enabled=true)";

@AttributeDefinition(name = "Surplus Feed-In: State-of-Charge limit", description = "Start Surplus-feed-in if State-of-Charge is bigger than this limit")
int surplusFeedInSocLimit() default 90;
Expand Down

0 comments on commit 24b7e60

Please sign in to comment.