- Jelina-Überblick
- EMS Builder
- Konfigurationsbeispiele
Beispiel: Lastmanagement für Ladestationsgruppen
Dieses Beispiel zeigt, wie sich allein mit den allgemeinen Funktionen von Edge-EMS Builder – dynamischer Datensuche, Ausdrucksberechnung, Triggerschemata und Frequenzbegrenzung – eine Lastverteilungsstrategie für wechselnde Gerätezahlen ohne Skriptänderungen aufbauen lässt. Gerätehersteller und Namenspräfixe sind beispielhaft; ersetzen Sie sie durch die tatsächlichen Bezeichnungen Ihres Projekts.
1. Fachlicher Hintergrund
Abschnitt betitelt „1. Fachlicher Hintergrund“An einem Verteiler sind mehrere Ladestationen mit OCPP oder herstellerspezifischen Protokollen sowie notwendige Verbraucher wie Beleuchtung und Klimaanlage angeschlossen. Die netzseitige Anschlusskapazität ist begrenzt. Laden alle Stationen gleichzeitig mit voller Leistung, kann der Gesamtverbrauch die Nennkapazität überschreiten und eine Abschaltung oder Mehrkosten wegen Leistungsüberschreitung verursachen.
Ziel: Notwendige Verbraucher werden vorrangig versorgt; alle online verfügbaren Ladestationen teilen sich die verbleibende Kapazität. Der Ladestrom jeder Station wird dynamisch angepasst. Beim Zu- oder Abschalten einer Station erfolgt innerhalb weniger Sekunden eine Neuverteilung. Zusätzliche Ladestationen erfordern keine Änderungen an Strategieskripten.
2. Leistungsmodell
Abschnitt betitelt „2. Leistungsmodell“Das System besitzt zwei zentrale Messpunkte:
Grid_total_power: aktuelle gesamte netzseitige Eingangsleistung einschließlich aller Verbraucher im Verteiler: notwendige Lasten, Ladestationen und weitere Lasten;- Notwendige Lasten sind bereits in diesem Messwert enthalten. Daher genügt es, den Gesamtverbrauch der Ladestationen zu steuern, um die gesamte Eingangsleistung unter der Nennleistung zu halten und notwendige Lasten vorrangig zu versorgen.
-- Verteilbare Reserve (kW) = (Nennleistung - gemessene Netzgesamtleistung) × Sicherheitsfaktor-- 10 % Sicherheitsreserve verhindern ein Überschwingen; notwendige Lasten sind im Messwert enthalten und werden nicht nochmals abgezogenlocal avail_kw = (rated_kw - grid_kw) * 0.9local per_A = (avail_kw / n) * 1000 / voltage_V -- Gleichmäßig aufgeteilten Strom berechnen (A)3. Dynamische Geräteerkennung: neue Ladestationen ohne Skriptänderung
Abschnitt betitelt „3. Dynamische Geräteerkennung: neue Ladestationen ohne Skriptänderung“Die Strategie setzt keine feste Anzahl von Ladestationen voraus. Bei jeder Ausführung erkennt eine Sammelabfrage nach Präfix und Suffix automatisch alle online verfügbaren Ladestationen:
local online = {}
-- Markenpräfixe nacheinander durchsuchen; bei Bedarf ergänzen oder entfernenfor _, brandPrefix in ipairs({"VENDORA", "VENDORB"}) do local statusMap = getLatestDataValuesByPrefixAndSuffix("Collection", brandPrefix, "_status") if statusMap then -- Die Rückgabe ist eine nach dataKey indizierte Tabelle: mit pairs() iterieren und auf den Sentinel-String "__Null__" prüfen for dataKey, dto in pairs(statusMap) do if dto.DataValue ~= "__Null__" then table.insert(online, dto) end end endendSobald die Erfassungsdatenpunkte einer neuen Ladestation gemäß den vereinbarten Namensregeln eingebunden sind (siehe „Namensregeln“ unten), wird sie beim nächsten Trigger automatisch in die gleichmäßige Verteilung einbezogen. Weder Skriptänderung noch Dienstneustart sind erforderlich. Diese Fähigkeit entsteht aus der Kombination von Datenmodell und Lua-Engine, nicht aus einer speziellen Einzelfunktion.
4. Auslösemodell: Datenaktualisierung und Frequenzbegrenzung im Skript
Abschnitt betitelt „4. Auslösemodell: Datenaktualisierung und Frequenzbegrenzung im Skript“Rein periodische Abfragen können redundante Schreibzugriffe verursachen, obwohl sich die Daten nicht geändert haben. Wird zusätzlich die Frequenzbegrenzung im Trigger abgebildet, werden die Bedingungen komplexer. Hier wird ein einfacheres Modell verwendet:
Auslösung bei Datenaktualisierung plus zeitstempelbasierte Frequenzbegrenzung im Skript: Der Trigger erkennt nur die Aktualisierung; das Skript steuert selbst, wie häufig tatsächlich Befehle gesendet werden.
Datenaktualisierung → Bedingung erfüllt → Skript auslösen → Aggregationspunkt last_ctrl_ms lesen (Zeitstempel der letzten Ausführung) → Vergangene Zeit < 5000ms: sofort zurückkehren, keinen Befehl senden → Vergangene Zeit ≥ 5000ms: Steuerung ausführen → Befehl schreiben → Zeitstempel aktualisierenVorbereitung der Bereitstellung: Erstellen Sie zunächst manuell einen Aggregationsdatenpunkt als „Gedächtnis“ des Skripts:
{ "dataKey": "last_ctrl_ms", "dataName": "充电控制上次执行时间戳", "expression": "return 0", "collectionCycle": -1}5. Entscheidungstabelle je Ladestation
Abschnitt betitelt „5. Entscheidungstabelle je Ladestation“Nach jeder Auslösung führt das Skript folgende Schritte aus: Netzparameter lesen → Frequenzbegrenzung prüfen → verfügbare Ladestationen suchen → verfügbaren Strom berechnen → je Station entscheiden → Zeitstempel aktualisieren → Befehlsliste zurückgeben. Entscheidungsregeln:
| Bedingung | Ladestationszustand | Aktion |
|---|---|---|
| Überlast oder gleichmäßig zugeteilter Strom < Mindeststrom | Lädt | Ladevorgang stoppen (stop_transaction) |
| Überlast oder gleichmäßig zugeteilter Strom < Mindeststrom | Anderer Zustand | Überspringen |
| Keine Überlast und gleichmäßig zugeteilter Strom ≥ Mindeststrom | Lädt | Neue Stromgrenze senden (set_charging_profile) |
| Keine Überlast und gleichmäßig zugeteilter Strom ≥ Mindeststrom | Lädt nicht | Ladevorgang starten (start_transaction; gültiger Autorisierungs-Tag erforderlich) |
Zentraler Skriptausschnitt als Triggeraktion, die ein Array von Schreibbeschreibungen zurückgibt:
local OffsetDateTime = require("OffsetDateTime")
local ratedKw = tonumber(${Grid_current} or 0) * tonumber(${Grid_voltage} or 0) * 0.001local gridKw = tonumber(${Grid_total_power} or 0)local overload = gridKw >= ratedKw
local voltage = tonumber(${Grid_voltage} or 1)local minChargePower = tonumber(${min_charge_power} or 4200)
local resultList = {}if #online > 0 then local availKw = (ratedKw - gridKw) * 0.9 local perA = math.floor((availKw / #online) * 1000 / voltage) local minA = math.floor(minChargePower / voltage)
for _, pile in ipairs(online) do local prefix = pile.DataKey:gsub("_status$", "") -- Ladesäulenpräfix aus dataKey ableiten, beispielsweise VENDORA01 if overload or perA < minA then if pile.DataValue == "Charging" then table.insert(resultList, buildCommonWriteMap("Collection", prefix .. "_type", "stop_transaction", nil, nil, false)) end else if pile.DataValue == "Charging" then table.insert(resultList, buildCommonWriteMap("Collection", prefix .. "_periods", '[{"start":0,"limit":' .. perA .. '}]', nil, nil, false)) elseif ${id_tag} ~= "__Null__" then table.insert(resultList, buildCommonWriteMap("Collection", prefix .. "_type", "start_transaction", nil, nil, false)) end end endend
table.insert(resultList, buildCommonWriteMap("Aggregation", "last_ctrl_ms", OffsetDateTime.now():unixMilli(), nil, nil, false))return resultList6. Namensregeln als Voraussetzung für die Sammelerkennung
Abschnitt betitelt „6. Namensregeln als Voraussetzung für die Sammelerkennung“Das Skript sucht Ladestationsdatenpunkte gesammelt im Format {Herstellerpräfix}{zweistellige Nummer}_{Attribut}. Folgende Regeln sind strikt einzuhalten:
| Bestandteil | Regel | Beispiel |
|---|---|---|
| Herstellerpräfix | Nur Großbuchstaben, keine Unterstriche | VENDORA, VENDORB; durch die tatsächlichen Herstellerkennungen ersetzen |
| Nummer | Zweistellig mit führender Null | 01, 02…10 |
| Attribut | Siehe folgende Tabelle | status, type |
Erforderliche Erfassungsdatenpunkte je Ladestation, auszugsweise:
| Attribut | Vollständiger dataKey als Beispiel | Lesen/Schreiben | Beschreibung |
|---|---|---|---|
_status |
VENDORA01_status |
Lesen | Ladestationszustand und Grundlage der Suche; fehlt dieser Punkt, wird die Station nicht erkannt |
_transaction_id |
VENDORA01_transaction_id |
Lesen | Aktuelle Transaktions-ID |
_type |
VENDORA01_type |
Schreiben | Befehlstyp: start/stop/set_charging_profile |
_periods |
VENDORA01_periods |
Schreiben | JSON mit Begrenzungszeiträumen, z. B. [{"start":0,"limit":16}] |
Für die sechste Ladestation dieses Herstellers genügt es, ihre Datenpunkte als VENDORA06_status, VENDORA06_type usw. anzubinden. Sie wird automatisch in die gleichmäßige Verteilung einbezogen.
7. Künftige Änderungen berücksichtigen
Abschnitt betitelt „7. Künftige Änderungen berücksichtigen“| Szenario | Skriptänderung erforderlich? | Vorgehen |
|---|---|---|
| Zusätzliche Ladestation desselben Herstellers | Nein | Datenpunkte gemäß den Namensregeln hinzufügen |
| Nennkapazitätsgrenze anpassen | Nein | Wert des Aggregationsdatenpunkts Grid_current ändern |
| Mindestladeleistung anpassen | Nein | Wert des Aggregationsdatenpunkts min_charge_power ändern |
| Ladestation vorübergehend offline | Nein | Nach Trennung wird _status leer; die Station entfällt automatisch aus der Onlineliste |
| Neuer Ladestationshersteller | Ja | Präfix zur Suchliste hinzufügen, z. B. {"VENDORA", "VENDORB", "VENDORC"} |
| Sicherheitsreserve konfigurierbar machen | Ja, einmalig | Aggregationsdatenpunkt für den Faktor anlegen und das Skript auf das Lesen dieses Punkts umstellen |
Die Tabelle verdeutlicht einen Grundsatz des Entwurfskonzepts: Die meisten fachlichen Änderungen erfordern nur eine Konfigurationsanpassung; wenige strukturelle Änderungen benötigen Skriptanpassungen. Deren Umfang bleibt vorhersehbar und beherrschbar.
8. Kombination mit weiteren Funktionen
Abschnitt betitelt „8. Kombination mit weiteren Funktionen“- Verteilen sich Ladestationen auf mehrere Unterverteiler, können deren gemessene Netzleistungen über Gerätekoordination an das Hauptgerät freigegeben werden. Dort erfolgt die globale gleichmäßige Verteilung.
- Kommen laufend weitere Hersteller und Modelle hinzu und ähneln sich die Ladestationsgruppen verschiedener Projekte, können UDE-Vorlagen Herstellerpräfix, Namensregeln und zugehörige Datenpunkte wiederverwendbar bündeln und den Aufbau neuer Projekte verkürzen.
- Für Stromgrenzwerte aus der Cloud und die Übersicht des Lastmanagementzustands können über Cloud-Edge-Koordination Netzreserven und Befehlsprotokolle je Ladestation regelmäßig gemeldet werden.
Diese Abbildung basiert auf der Konfigurationslogik dieser Seite und zeigt Daten- und Steuerungsabläufe. Konkrete Felder entnehmen Sie den beschriebenen Konfigurationen und Skripten.