Einstieg in openEMS

Hallo liebe openEMS-Community,
Ich mache im Zuge meiner Diplomarbeit meine ersten Schritte mit openEMS und Java-Programmierung. Nach längerer Recherche über openEMS soll es nun an die Entwicklung einer openEMS-Konfiguration gehen, für stationäre Energiespeicher. Unser System besteht aktuell aus einem Batteriespeicher, einem BMS von Baumaco, dem Umrichter refu88k und einem Zweirichtungszähler.
Zusammen mit meinen Betreuern wurden diese Komponenten in die openEMS-Software eingebunden. Die Software läuft auf einem Zielrechner über eine VirtualMachine mit einem Linux-Betriebssystem. Die Kommunikationsschnittstellen werden direkt an den Zielrechner angeschlossen, wodurch wir uns die Werte der Batterie und des Umrichters (refu88k) anzeigen lassen können.
Jetzt haben wir das Problem, dass die Anzeige der Werte über das Befehlsfenster „Terminator“ geschieht und diese sehr unübersichtlich ist. Dadurch können wir nicht genau sagen, welche Werte zu welchen Kanälen der einzelnen Komponenten gehören. Wir wissen, dass einzelne Werte der Komponenten über das openEMS-UI abgerufen werden können. Da dies aber auf dem Zielsystem nicht verfügbar ist und auch nicht Bestandteil meiner Diplomarbeit, können wir darauf nicht zurückgreifen und suchen nach einer anderen Lösung. Gibt es eine andere Möglichkeit Werte der einzelnen Kanäle einer Komponente außerhalb des UI, z.B. in der AppacheWeb-Anwendung, anzeigen zu lassen?

Interessant für uns wäre auch zu wissen, ob Daten die empfangen wurden gesichert werden können und im späteren Verlauf wieder in in das System eingespeist werden können, zur Weiterverarbeitung (Simulationszwecke, Stichwort: Playback)?

Ich bin sehr dankbar über die Hilfe der Community!

Mit freundlichen Grüßen,
Con Seifert

Hallo Con,

das ist ein spannendes Projekt. Halte uns gerne über den Fortschritt am Laufenden und - wenn möglich - wäre es toll, wenn die Diplomarbeit am Ende offen einsehbar wäre. Natürlich wäre es auch schön, wenn der entstandene Quellcode am Ende auch über einen Pull-Request auf Github seinen Weg ins Projekt finden würde.

Ich kann dir das OpenEMS UI empfehlen, um darüber das System benutzerfreundlich zu monitoren und Einstellungen zu bearbeiten. Leider ist die Doku dazu, wie man das OpenEMS UI auf einem Linux-System einrichtet noch nicht fertig - vielleicht hilft dieser Forumseintrag erstmal weiter: UI am Raspberry konfigurieren - #9 by stefan.feilmeier

Über das OpenEMS UI gibt es eine Möglichkeit, sich alle aktuellen Echtzeitdaten einer Komponente als Excel-Datei herunterzuladen - siehe Export Channels to Excel by sfeilmeier · Pull Request #1526 · OpenEMS/openems · GitHub.

Ansonsten hat es sich zur Auswertung bewährt, die Daten in eine Zeitreihendatenbank (InfluxDB) zu schreiben und dann über Grafana zu visualisieren, dazu:

  1. InfluxDB in Version 1.x installieren
  2. Grafana installieren (beide stehen z. B. bequem als Debian-Pakete zur Verfügung)
  3. Die InfluxDB-Komponente im OpenEMS Edge aktivieren (openems/io.openems.edge.timedata.influxdb at develop · OpenEMS/openems · GitHub).

Alle Daten werden dann automatisch in die InfluxDB geschrieben. Über Grafana können individuelle Dashboards erstellt werden. Über Grafana ist es auch Möglich die Daten als CSV-Dateien zu exportieren, die dann wieder als Input für die Simulator-Komponenten in OpenEMS verwendet werden können.

Viele Grüße,
Stefan

Hallo Stefan,

vielen Dank für die schnelle Rückmeldung und Hilfe!

Die Diplomarbeit wird mit sehr hoher Wahrscheinlichkeit am Ende mit einem Sperrvermerk versehen werden müssen. Aus diesem Grund wird diese erst einmal nicht offen einsehbar sein. Ich werde trotzdem versuchen euch so gut ich kann, sofern es mir erlaubt ist, auf dem laufenden zu halten.

Wir werden nun versuchen, deine Vorschläge in die Entwicklung der EMS-Konfiguration so gut es uns gelingt einfließen zu lassen. Bei weiteren Fragen die im laufe der Entwicklung entstehen werden oder falls es uns nicht gelingen sollte deine Vorschläge umzusetzen, werde ich mich wieder melden! Natürlich auch wenn wir zu einem erfolgreichen Ergebnis gelangt sind. :wink:

Mit freundlichen Grüßen,
Con

1 Like

Hallo Stefan,

die Implementierung von InfluxDB und Grafana in unser Projekt hat weitestgehend problemlos funktioniert. Wir sind jetzt in der Lage Werte von Umrichter, BMS und Zähler aufzunehmen und in sinnvoller Weise darzustellen (Diagramme).
Wir konnten auch schon erste kleine Tests fahren in denen wir die Batterie geladen haben. Beim entladen gab es allerdings noch ein Problem.

Wir verwenden aktuell für das Laden und Entladen den Controller „ESS One Full Cycle“. Geben wir für die Leistung einen negativen Wert ein wird dieser vom EMS erkannt und es gibt keine Fehlermeldungen. Allerdings unterschreitet der Refu88k nicht 0W beim entladen (können wir jetzt schön in Grafana sehen). Wir gehen davon aus, dass die Entladeleistung durch irgendetwas oder in irgend einer Form auf 0 limitiert wird.

Haben Sie dazu noch eine Idee an was das liegen könnte?

Mit freundlichen Grüßen,
Con Seifert

Hallo Con,

ich gehe davon aus, dass ihr in eine Konfiguration mit REFUstore 88K BatteryInverter (openems/io.openems.edge.batteryinverter.refu88k at develop · OpenEMS/openems · GitHub) und Generic ESS (openems/io.openems.edge.ess.generic at develop · OpenEMS/openems · GitHub) verwendet, oder? Welche Batterie wird genutzt?

Die Leistungsverteilung nutzt intern ein lineares Gleichungssystem, dem Nebenbedingungen (Constraints) hinzugefügt werden, die die möglichen Leistungsvorgaben einschränken. Constraints kommen z. B. von

Pauschal lässt sich die Frage deshalb ohne mehr Informationen leider nicht beantworten. Hilfreich wäre z. B. ein Detailed-Log vom ess0, batteryInverter0 und battery0 und die Info, welche Controller/Scheduler laufen.

Übrigens wäre auch der ESS Linear Power Band Controller euren Einsatzzweck gut anwendbar: openems/io.openems.edge.controller.ess.linearpowerband at develop · OpenEMS/openems · GitHub

Gruß,
Stefan

Hallo Stefan,

unsere Konfiguration besteht wie du es oben beschrieben hast aus dem REFUstore88K BatteryInverter und dem Generic ESS (Wäre es besser wenn wir für das ESS die angelegte Komponente „in.openEMS.edge.ess.refu“ verwenden anstatt des Generic ESS? Worin besteht der Unterschied, kannst du mich dahingehend aufklären?). Wir benutzen eine selbst aufgebaute Batterie mit BMW-Modulen und einem BMS von der Fa. Baumaco.

Ich werde mir die Constrains genauer ansehen und vielleicht finde ich einen Wert, der die Leistung zum Entladen der Batterie begrenzt.
Danke für diesen Hinweis! :slight_smile:

———————————————————————————————————————————

Im laufe der Vergangenen Tage habe ich mir den Controller „ESS One Full Cycle“ auch nochmal genauer angeschaut um zu verstehen wie und unter welchen Bedingungen die Batterie geladen/entladen wird.
Dabei kam mir der Gedanke, dass der Controller keine negativen Werte verarbeiten kann. Denn die Entscheidung ob geladen oder entladen wird hängt vom SoC der Batterie ab nicht davon ob eine negative oder positive Leistung angegeben wird. Sprich der Controller schaut, ist der SoC größer oder kleiner 50% und entlädt bzw. lädt die Batterie zuerst voll/leer. Danach startet erst der eigentliche Zyklus des laden/entladen. Wenn dieser durchlaufen wurde, wird der eingegebene Wert für die Leistung auf 0 gesetzt und der Vorgang ist beendet.
Nach meinem Verständnis können wir also nicht einfach sagen, die Batterie soll entladen werden, sondern wir müssen warten bis diese voll geladen wurde und erst dann wird sie entladen?!

Bitte korrigiere mich wenn ich damit falsch liege.

———————————————————————————————————————————

Den Daten-Log kann ich dir aktuell leider nicht zur Verfügung stellen, weil mir dieser selber nicht vorliegt. Sobald ich diesen habe, werde ich die Informationen gerne mit dir teilen!

Den „ESS Linear Power Band Controller“ habe ich auch schon entdeckt und stimme dir zu, dass wir unsere System mit diesem auch nochmal testen können.

Mit freundlichen Grüßen,
Con Seifert

Ok, interessant - danke für die Hintergrundinfos. REFU ist übrigens Mitglied in der OpenEMS Association, falls ihr aus der Richtung Unterstützung braucht. Bei FENECON arbeiten wir auch mit REFU und BMW-Batterien, so dass da natürlich auch im Sinne von Open-Source jeder Informationsaustausch oder Pull-Requests mit Verbesserungen sehr gerne gesehen sind.

Den „ESS One Full Cycle“ nutzen wir z. B. gelegentlich für Kapazitätstests, deshalb ist die Logik so, wie beschrieben. Vermutlich ist es aber für euch am einfachsten, wenn ihr euch einen eigenen spezifischen Controller erstellt, für die Tests die ihr machen wollten. Alternativ ist auch ein FixActivePower-Controller sehr gut geeignet; dieser lässt sich auch schön über das OpenEMS UI steuern.

Gruß,
Stefan

Hallo Stefan,

mit hoher Wahrscheinlichkeit werden wir für unsere Tests einen/mehrere eigene Controller erstellen müssen. Sobald wir dazu weitere Erkenntnisse haben und ein Ergebnis vorlegen können werde ich dieses so gut ich kann mit dir teilen.

Wenn Ihr bei FENECON auch mit Refu und BMW-Batterien arbeitet, welches ESS verwendet ihr in eurer Konfiguration? Ist es besser das Generic ESS zu verwenden oder die Komponente „in.openems.edge.refu“?
Ich frage dies weil wir aktuell vor der Entscheidung stehen, welche von den beiden Komponenten für unsere Konfiguration am besten geeignet ist und welche Unterschiede zwischen den beiden Komponenten bestehen?

Vielleicht kannst du mir dazu etwas weiterhelfen. :slight_smile:

Mit freundlichen Grüßen,
Con Seifert

Hallo Con,

in.openems.edge.refu war eine alte Implementierung für einen früheren Batteriewechselrichter von REFU. Davon wurden aber insgesamt nur wenige verbaut und die Implementierung ist mittlerweile auch aus dem develop branch geflogen.

BatteryInverter REFU und Generic-ESS sind also die beste Wahl.

Gruß,
Stefan

1 Like

Guten Morgen Steffan,

ich habe vor kurzer Zeit einen neuen Controller zum Laden und Entladen der Batterie in OpenEMS implementiert. Die Implementierung verlief weitestgehend reibungsfrei und wir können den Controller ohne Fehlermeldungen „Bauen“.
Lediglich die „Test-Datein“ (siehe Hierarchie Controller) geben noch Fehler zurück, aber diese können ignoriert werden oder?

———————————————————————————————————————————

Wenn wir die Konfiguration mit dem neuen Controller nun auf unserem Zielsystem ausführen und einen beliebigen Wert für die Leistung einstellen (egal ab größer, kleiner oder gleich null) beginnt der Refu88k zu arbeiten. Nach wenigen Sekunden springt der Umrichter von dem Betriebsbereiten Zustand in einen Fehlerzustand über und gibt folgenden Fehler aus:
„DC-Leistung nicht ausreichend“.
In Terminator lassen wir uns zudem einzelne Werte des Controllers zurück geben. Der Wert „State“ steht dabei auf „Fault“ und es wird der Fehler „Running the Controller failed“ ausgegeben.
Ergebnis: Der Controller funktioniert nicht!

Folgende Controller sind in der Konfiguration aktiviert:

  • Battery-Inverter refustore88k
  • Baumaco BMS
  • Bridge Modbus RTU Serial
  • Bridge Modbus TCP
  • Controller API REST/JSON
  • Controller API Websocket
  • Controller Debug Detailed Log
  • Controller Debug Log
  • Controller ESS Charge Discharge (mein selbst programmierter Controller)
  • Core Host
  • ESS Generic Managed Symmetric
  • Meter Microcare SDM 630
  • org.ops4j.pax.logging
  • Scheduler All Alphabetically
  • Timedata Influx DB

Kannst du mir bei diesem Problem weiter helfen? Ist unsere Konfiguration vielleicht kaputt oder ist der Quellcode für meinen Controller fehlerhaft?

Ich komme an dieser Stelle nicht weiter und hoffe du kannst mir weiterhelfen.

———————————————————————————————————————————
Quellcode Controller:
package io.openems.edge.controller.ess.chargedischarge;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

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.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.edge.common.component.AbstractOpenemsComponent;
import io.openems.edge.common.component.ComponentManager;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.controller.api.Controller;
import io.openems.edge.controller.ess.chargedischarge.ChargeDischarge;
import io.openems.edge.ess.api.ManagedSymmetricEss;
import io.openems.edge.ess.power.api.Phase;
import io.openems.edge.ess.power.api.Pwr;

@Designate(ocd = Config.class, factory = true)
@Component(//
name = “Controller.Ess.ChargeDischarge”, //
immediate = true, //
configurationPolicy = ConfigurationPolicy.REQUIRE)
public class ChargeDischarge extends AbstractOpenemsComponent implements Controller, OpenemsComponent {

private final Logger log = LoggerFactory.getLogger(ChargeDischarge.class);


@Reference
protected ComponentManager componentManager;

private Config config;
//initialization of variables
int current_power_charge;			//current calculated power
int duration; 						//duration of the local state
int current_target_power;			//current target power which is set in AppacheFelixWebInterface
int maxChargePower;					//ESS max Charge Power
int maxDischargePower;				//ESS max Discharge Power
int current_target_power_charge;	//current power which is set
int target_power;					//power which is set in Appache Felix
int step;							//step by which performance is increased							

/*
 public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
 
 
 private final Doc doc;
 
 private ChannelId(Doc doc) { this.doc = doc; }

  @Override public Doc doc() { return this.doc; } }
 */

public ChargeDischarge() {
	super(//
			OpenemsComponent.ChannelId.values(), //
			Controller.ChannelId.values()//, //
			//ChannelId.values() //
	);
			
}

@Activate
void activate(ComponentContext context, Config config) {
	super.activate(context, config.id(), config.alias(), config.enabled());
	this.config = config;
	
	//initialization of variables
	current_power_charge =0;
	duration = 0;
	current_target_power = 0;
	target_power = 0;
	step = this.config.step_size();
	maxChargePower = 0;	
	maxDischargePower = 0;		

}

@Deactivate
protected void deactivate() {
	super.deactivate();
}

@Modified
void modified(Config config) throws OpenemsNamedException {
	this.config = config;
	step = this.config.step_size();
}

//print debug-log
@Override
public String debugLog() {
	return "Hello World from Controller ChargeDischarge"+ //
			
			" ESS max. Discharge Power " + this.maxDischargePower + //	
			" ESS max. Charge Power " + this.maxChargePower + //
			" current Power " + this.current_power_charge + //
			" target Power " + this.target_power + //
			" current Target Power " + this.current_target_power + //
			"set active Power" + this.current_target_power_charge ; //		
}


@Override
public void run() throws OpenemsNamedException {
	

	ManagedSymmetricEss ess = this.componentManager.getComponent(this.config.ess_id());
	
	// get max charge/discharge power
	maxDischargePower = ess.getPower().getMaxPower(ess, Phase.ALL, Pwr.ACTIVE);
	maxChargePower = ess.getPower().getMinPower(ess, Phase.ALL, Pwr.ACTIVE);
	
	
	int ctp = this.calculate_current_target_power(step, current_power_charge, maxChargePower, duration, maxDischargePower); //function to calculate_current_target_power
	
	ess.setActivePowerLessOrEquals(ctp);		//set the calculated power at the output of the ess
}

private int calculate_current_target_power( int step, int current_power_charge, int maxChargePower, int duration, int maxDischargePower ) throws OpenemsNamedException {
target_power = this.config.target_power();

//1) ensure max ramp steepness
//2) ensure respecting system limit
//3) ensure reaching and holding of target power
//4) ensure duration of target power active

int sign = -1;
if (target_power >= 0)
{		
	sign = 1;
}
target_power = Math.abs(target_power);	

if (current_power_charge < target_power) {
	current_power_charge += step;			//1)3)calculate the current power for charging
}

if (sign == -1)
{		
current_target_power = Math.min(current_power_charge, maxDischargePower);//2)
}
current_target_power = Math.min(current_power_charge, maxChargePower);//2)

if (current_power_charge >= target_power) { //4)
	duration = duration +1;
	current_target_power = target_power;
}

if (duration == 10) { //4)
	duration = 0;
	current_target_power = 0;
}		
		
return -current_target_power*sign;

}

}
———————————————————————————————————————————
Quellcode Config Controller:
package io.openems.edge.controller.ess.chargedischarge;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

@ObjectClassDefinition(//
name = “Controller Ess Charge Discharge”, //
description = “charges and discharges the battery at a certain SOC”)
@interface Config {

@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
String id() default "ctrlChargeDischarge0";

@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
String alias() default "";

@AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?")
boolean enabled() default true;

@AttributeDefinition(name = "Ess-ID", description = "ID of Ess device.")
String ess_id();

@AttributeDefinition(name = "Charging Power [W]", description = "Power to charge and discharge the battery")
int target_power() default 0;

@AttributeDefinition(name = "Step-size [W]", description = "Step by which performance is continuously increased")
int step_size() default 100;

String webconsole_configurationFactory_nameHint() default "Controller io.openems.controller.ChargeDischarge [{id}]";

}

Für einen besseren Datenaustausch finden Sie hier meine E-Mail Adresse und meine Telefonnummer.

Con.Seifert@fes-aes.de
0375 56605022

Richtig, wobei sich für uns ein Test-Driven-Ansatz bewährt hat, bei dem wir die Controller-Logik zuerst in einem solchen JUnit-Test simulieren, bevor wir damit an die Hardware gehen.

Das deutet darauf hin, dass das System zumindest am Anfang richtig startet. Der Generic-ESS durchläuft dabei die folgende State-Machine. Dafür ist aber notwendig, dass der BatteryInverter und die Battery immer laufen - sonst springt die State-Machine in das ErrorHandling. Welche Komponente zeigen denn genau einen Fehler (State Fault)? Der BatteryInverter, der Generic-ESS oder der Controller?


Quelle: https://github.com/OpenEMS/openems/blob/develop/io.openems.edge.ess.generic/doc/statemachine.md

Die Meldung “Running the Controller failed” bedeutet, dass der Controller eine Exception wirft. Hier der Code dazu: https://github.com/OpenEMS/openems/blob/develop/io.openems.edge.core/src/io/openems/edge/core/cycle/CycleWorker.java#L128. Ich würde an deiner Stelle mal einen Breakpoint im Controller setzen und schrittweise durchgehen um zu sehen, wo eine Exception geworfen wird. Auf den ersten Blick kann ich keine gravierenden Fehler sehen. Es könnte sein, dass ManagedSymmetricEss ess = this.componentManager.getComponent(this.config.ess_id()); in den ersten Cycles null zurück gibt, wenn der ESS noch nicht aktiviert wurde (deshalb sollte man hier besser mit @Reference arbeiten, dann ist die Startreihenfolge garantiert), aber das ist nicht das Fehlerbild, das du beschreibst.

Hallo Herr Feilmeier,

nach mehrfachen Inbetriebnahmeversuchen des System, habe ich erfahren, dass das System (ESS) eine Precharging-Phase bei jedem start durchläuft. Somit ist der Fehler den ich oben beschrieben habe gar kein Fehler, sondern eine Routine die bei jedem Start abgearbeitet wird und mir nicht bekannt war.
Ist diese Phase durchlaufen und es ist kein Controller aktiv der die Batterie lädt oder entlädt ist die Reaktion des Refu, dass die DC-Leistung zu gering ist, nur logisch.

Ich habe nach dieser Phase den OpenEMS-Controller „ESS.FixActivePower“ aktiviert und sowohl eine negative als auch positive Leistung zum laden und entladen vorgegeben. Beide male wurde die Funktion korrekt umgesetzt und die Batterie geladen und entladen.
Der Refu und unserer Konfiguration funktioniert also.

Lediglich meine Programmierung ist derzeit noch das Problem. Aber daran arbeite ich. :wink:

Ich schätze zum aktuellen Zeitpunkt, dass die Werte aus der Konfiguration und aus unserer Berechnung nicht richtig übernommen werden bzw. die Funktion „Run“ nicht durchlaufen wird.
Anhaltspunkt ist der DebugLog, den wir uns für unserer Werte die wir übergeben bzw. berechnen, anzeigen lassen. Darin sind permanent alle Werte 0.

Wir versuchen nun dieses Problem zu beheben.
Falls wir erfolg haben oder ich weiter Fragen habe melde ich mich wieder.

Vielen Dank für Ihre Hilfe und die hilfreichen Lösungsansätze! :slight_smile:

Mit freundlichen Grüßen,
Con Seifert

Hallo Herr Feilmeier,

nach längerer Suche habe ich endlich die Ursache gefunden, warum der von uns selbst erstellte Controller nicht funktionierte.
Der DebugLog zeigte im Bedienfeld von Terminator immer die Fehlermeldung, dass die ID des Controllers nicht zugeordnet werden konnte und somit auch der Controller nicht erkannt/aktiviert wurde. Nachdem ich nun die ID händisch im Apache-Felix-Web-Interface erneut eingegeben habe (sowohl in unserem als auch im DebugLog Controller) wurde der Controller erkannt und funktioniert. :slight_smile:

Können Sie mir sagen warum die ID des Controllers die in der „config.java“ steht nicht erkannt wird? Auch wenn ich einen neuen „default-Wert“ für die ID festlege, wird dieser nicht übernommen.

Vielen Dank für Ihre Hilfe und Unterstützung!

Mit freundlichen Grüßen,
Con Seifert

Hallo,

wir hatten kürzlich ähnliche Probleme mit OSGi References… möglicherweise hängt es damit zusammen und dieser Commit behebt das Problem:

1 Like

Hallo Herr Feilmeier,

Vielen Dank für diesen Hinweis.
Ich werde bei der Integration neuer Komponenten zukünftig darauf achten, die Zieleigenschaften zurückzusetzen.

Mit freundlichen Grüßen,
Con Seifert

Hallo Herr Feilmeier,

bei dem Versuch unsere Batterie zu laden bzw. zu entladen, sind wir auf ein Problem mit der Konfiguration unsres Controllers gestoßen bei dem wir unbedingt ihre Hilfe benötigen.
Wir verwenden für das Laden und Entladen der Batterie den Controller “ESS.FixAcivePower”. Diesen haben wir so angepasst, dass er eine Rampe mit einer frei wählbaren Schrittweite fährt bis die gewünschte Leistung zum Laden und Entladen erreicht ist. Sobald diese erreicht ist wird die gewünschte Leistung konstant gesetzt.
Nun wollten wir die Leistung von bspw. 1000W auf 2000W anheben. Wir sind davon ausgegangen, dass einfach bei 1000W die Leistung in Form unserer Rampe weiter erhöht wird.
Aktuell ist es aber so, dass wenn wir den neuen Leistungswert von 2000W in unserer Konfiguration eingeben und danach auf “save” klicken alle Werte in der Konfiguration auf 0 gesetzt werden. Selbst die Variablen die mit einem Default-Wert versehen sind. Somit beginnt die Software das Laden der Batterie wieder von 0 auf 2000W auszuführen anstatt von 1000W auf 2000W.

Wird die Konfiguration mit jeder Eingabe oder Änderung von Werten neu aufgesetzt, also alle Variablen auf 0 gesetzt, oder übersehen wir an dieser Stelle etwas?

Vielen Dank im Voraus für Ihre Hilfe und Ihre Mühen

Mit freundlichen Grüßen,
Con Seifert

----------------------------------------------------------------------------------------------------------------------------------------Quellcode Controller:

package io.openems.edge.controller.ess.fixactivepower;

import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.metatype.annotations.Designate;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.edge.common.component.AbstractOpenemsComponent;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.controller.api.Controller;
import io.openems.edge.ess.api.ManagedSymmetricEss;
import io.openems.edge.ess.power.api.Phase;
import io.openems.edge.ess.power.api.Pwr;

@Designate(ocd = Config.class, factory = true)
@Component(//
name = “Controller.Ess.FixActivePower”, //
immediate = true, //
configurationPolicy = ConfigurationPolicy.REQUIRE //
)
public class EssFixActivePowerImpl extends AbstractOpenemsComponent
implements EssFixActivePower, Controller, OpenemsComponent {

@Reference
private ConfigurationAdmin cm;

@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
private ManagedSymmetricEss ess;

private Config config;


//declaration of variables
int current_power_charge;			//current calculated power
int current_target_power;			//current target power which is set in AppacheFelixWebInterface
int target_power;					//power which is set in Apache Felix
int step;							//step by which performance is increased, set in Apache Felix
int maxChargePower;
int maxDischargePower;
int target_power_old;




public EssFixActivePowerImpl() {
	super(//
			OpenemsComponent.ChannelId.values(), //
			Controller.ChannelId.values(), //
			EssFixActivePower.ChannelId.values() //
	);
	
	//initialization of variables
			current_power_charge =0;
			current_target_power = 0;
			target_power = 0;
			step = 0;
			target_power_old =0;
}

@Activate
void activate(ComponentContext context, Config config) {
	super.activate(context, config.id(), config.alias(), config.enabled());
	this.config = config;
	

	if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "ess", config.ess_id())) {
		return;
	}
}

@Deactivate
protected void deactivate() {
	super.deactivate();
}


//print debug-log
	@Override
	public String debugLog() {
		return "Hello World from Controller FixActivePower Modified"+ //	
				" current Power: " + this.current_power_charge +//
				" target Power: " + this.target_power +//
				" step: " + this.step + //
				" CTP: " + this.current_target_power;		
	}
	
@Override
public void run() throws OpenemsNamedException {
	switch (this.config.mode()) {
	case MANUAL_ON:
		// Apply Active-Power Set-Point
		//get stepsize and target power from the config
		target_power = this.config.power();
		step = this.config.step_size();
		
		//get max charge/discharge power
		maxDischargePower = ess.getPower().getMaxPower(ess, Phase.ALL, Pwr.ACTIVE); //range zero to positive value
		maxChargePower = ess.getPower().getMinPower(ess, Phase.ALL, Pwr.ACTIVE); //range zero to negative value
		
		if (target_power < 0)
		{
			//if the value, set in Apache Felix, of "power" is negative -> charge the battery 
			if ((current_power_charge > target_power) && (current_power_charge > maxChargePower)) 
			{
				current_power_charge = current_power_charge - step;
				current_target_power = current_power_charge;
			}
			else if (current_target_power <= target_power) //new 
			{
				current_target_power = target_power;//Math.max(target_power, maxChargePower); //the bigger value is set
				target_power_old = current_target_power; //new
			}
			
			//new
			if ((current_target_power < target_power) && (target_power > target_power_old)) 
			{
				current_target_power = current_target_power + step;
			}
			else if ((current_target_power >= target_power) && (target_power > target_power_old))
			{
				current_target_power = target_power;
				target_power_old = current_target_power;
			}
			
		}
		else if (target_power > 0)
		{
			//if the value, set in Apache Felix, of "power" is positive -> discharge the battery 
			if ((current_power_charge < target_power) && (current_power_charge < maxDischargePower)) 
			{
				current_power_charge = current_power_charge + step;
				current_target_power = current_power_charge;
			}
			else 
			{
				current_target_power = Math.min(target_power, maxDischargePower); //the smaller value is set
			}
		}
		else 
		{
			current_target_power = 0;
		}
		
		//set the current power 
		this.ess.setActivePowerEquals(current_target_power);
		
		sleep(2000);
		
		break;

	case MANUAL_OFF:
		// Do nothing
		break;
	}
}


   private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ignored) {
        }
    }	

}

----------------------------------------------------------------------------------------------------------------------------------------Quellcode Config

package io.openems.edge.controller.ess.fixactivepower;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

@ObjectClassDefinition( //
name = “Controller Ess Fix Active Power Modified”, //
description = “Defines a fixed charge/discharge power to a symmetric energy storage system.”)
@interface Config {

@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
String id() default "ctrlFixActivePower0";

@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
String alias() default "";

@AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?")
boolean enabled() default true;

@AttributeDefinition(name = "Mode", description = "Set the type of mode.")
Mode mode() default Mode.MANUAL_ON;

@AttributeDefinition(name = "Ess-ID", description = "ID of Ess device.")
String ess_id();

@AttributeDefinition(name = "Charge/Discharge power [W]", description = "Negative values for Charge; positive for Discharge")
int power();

@AttributeDefinition(name = "Step-size [W]", description = "Step by which performance is continuously increased")
int step_size() default 100;

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

String webconsole_configurationFactory_nameHint() default "Controller Ess Fix Active Power [{id}]";

}

Hallo,

wenn man in apache Felix Einstellungen ändert, so wird die methode (ich glaube) “reload” aufgerufen, hier muss definiert werden, was bei einer änderung passiert, wenn diese nicht existiert, so ist es wie bei einem deaktivieren und erneuten aktivieren der Componente, also beginnt die Logik von “vorne”.
Schönen Gruß

Paul

Hallo P. Wimmer,

vielen Dank für diesen Hinweis. :slight_smile:

Hallo,

standardmäßig werden Komponenten bei einer Neukonfiguration beendet und neu gestartet. Der Vorteil dieser Vorgehensweise ist, dass man nicht im Code auf sich ständig ändernde Konfigurationseinstellungen reagieren muss. Das gleiche gilt für mit @Reference statisch eingebundene Komponenten. Diese ändern sich ebenfalls nicht zur Laufzeit.

Wenn man möchte, dass die Konfiguration dynamisch geändert werden soll, ohne dass die Komponente beendet und neu gestartet wird, kann man die Annotation @Modified implementieren. Hier ein kleines Beispiel:

Gruß,
Stefan

1 Like

Hallo Herr Feilmeier,

vielen Dank für die Antwort. :slight_smile:
Aktuell sind die Anforderungen an unser EMS so definiert, dass es möglich sein soll die Konfiguration dynamisch zu ändern. Das dies über die Annotation @Modified umgesetzt werden kann, war mir an dieser Stelle noch nicht bekannt.
Ich werde unseren Quellcode dahingehend anpassen.

Mit freundlichen Grüßen
Con Seifert

1 Like