Skip to content

Fix Consumer: Absturz der Regelung durch defekte/gelöschte Verbraucher-Module verhindern - #3917

Open
seaspotter wants to merge 2 commits into
openWB:masterfrom
seaspotter:fix-consumer-module-errors
Open

Fix Consumer: Absturz der Regelung durch defekte/gelöschte Verbraucher-Module verhindern#3917
seaspotter wants to merge 2 commits into
openWB:masterfrom
seaspotter:fix-consumer-module-errors

Conversation

@seaspotter

Copy link
Copy Markdown
Collaborator

Problem 1: Verbraucher-Modul-Erstellung schlägt fehl -> Log-Spam und kaputte Persistenz

Beim Einrichten eines IDM-Wärmepumpen-Verbrauchers zeigte das Log dauerhaft (jeden Regelzyklus) zwei Fehler:

2026-09-09 16:15:40,728 - {modules.loadvars:74} - {ERROR:MainThread} - Fehler im loadvars-Modul bei Element 16
Traceback (most recent call last):
File "/var/www/html/openWB/packages/modules/loadvars.py", line 68, in _set_values
if ConsumerUsage.METER_ONLY.value in consumer.module.config.usage:
AttributeError: 'NoneType' object has no attribute 'config'


2026-09-09 16:15:40,994 - {helpermodules.setdata:345} - {ERROR:Setdata} - Unbekanntes set-Topic: openWB/set/consumer/18/module/simulation, {'timestamp': ..., 'power': 0.0, 'imported': 15049239.777815694, 'exported': 0}

Root Cause:

  • SimCounterConsumer (modules/common/simcount/_simcounter.py) persistiert seinen Zählerstand nach openWB/set/consumer/{id}/module/simulation. setdata.py::process_consumer_topic kannte dieses Topic nicht (anders als die analogen /get/simulation- bzw. .../component/.../simulation-Fälle für Chargepoint/Device) und löschte es sofort wieder als "unbekannt" - jeder Verbraucher mit eingebautem Zähler verliert damit seinen persistierten Zählerstand bei jedem Neustart.
  • subdata.pys /module-Pattern hatte keinen $-Anker und griff dadurch auch bei diesem gemirrorten .../module/simulation-Topic, wo es als vollständige Modul-Neukonfiguration interpretiert wurde -> KeyError: 'vendor', verschluckt als generisches "Fehler im subdata-Modul".
  • Wenn die Modul-Erstellung für einen Verbraucher (aus welchem Grund auch immer) fehlschlägt, blieb consumer.module dauerhaft None, unsichtbar für den Nutzer, und loadvars.py crashte bei jedem Zyklus erneut, statt den Verbraucher einmalig als fehlerhaft zu markieren.

Problem 2: Gelöschter Verbraucher blockiert die komplette Ladestromberechnung

Aus discussions#3908 (@Frank-Hoe):

Mit aktueller Alpha von heute und gemergter Verbrauchersteuerung habe ich testweise einen Verbraucher angelegt (nur Messung) [...] und, nachdem ich hier keinen Mehrwert [...] erkennen konnte, wieder gelöscht.
Obwohl er weder unter den Verbrauchern noch unter der Struktur sichtbar ist, wird er in der Prioritätenliste angezeigt (s. Screenshot).
Leider lädt auch das Fahrzeug jetzt nicht mehr ("Ladevorgang wird gestartet...", aber nicht passiert), weder PV noch Sofortladen. Zweifacher Neustart war erfolglos.

Und im mitgeposteten Log:

File "control/algorithm/algorithm.py", line 30, in calc_current
self.surplus_controlled.check_submode_pv_charging()
File "control/algorithm/surplus_controlled.py", line 135, in check_submode_pv_charging
for load in get_loads_by_chargemodes(CONSIDERED_CHARGE_MODES_PV_ONLY):
File "control/algorithm/filter_chargepoints.py", line 63, in _group_loads_by_chargemode
consumer = data.data.consumer_data[f"{item['type']}{item['id']}"]
KeyError: 'consumer13'

Root Cause:

  • remove_loadmanagement_prio_item() vergleicht die id mit ==. Kommt sie (z. B. aus einem MQTT-Kommando) als str statt int an, wird das Element nicht gefunden, eine IndexError geworfen - und dadurch bricht removeConsumer()/removeVehicle() ab, bevor die korrigierte Prioritätenliste erneut published wird. Der Eintrag bleibt dauerhaft im retained Topic stehen, auch über Neustarts hinweg (deckt sich exakt mit "wird in der Prioritätenliste angezeigt" trotz Löschung).
  • filter_chargepoints.py::_group_loads_by_chargemode löst so einen Eintrag mit einem ungeschützten dict[...]-Zugriff auf consumer_data auf - ungefangen, innerhalb von calc_current(). Ein einziger Karteileichen-Eintrag reicht damit aus, um die Stromberechnung für alle Ladepunkte in jedem Zyklus abzuschießen.

Fix

  1. setdata.py erkennt .../module/simulation jetzt als gültiges Topic.
  2. subdata.py: /module-Pattern mit $-Anker; Modul-Erstellung schlägt jetzt sichtbar fehl (Log mit Verbraucher/vendor/type, fault_state/fault_str, Systemmeldung an die UI) statt module stillschweigend auf None zu belassen.
  3. loadvars.py überspringt Verbraucher ohne erfolgreich erstelltes Modul, statt bei jedem Zyklus mit AttributeError abzubrechen.
  4. loadmanagement_prio.py: id-Vergleich tolerant gegen str/int-Mismatch; "nicht gefunden" ist eine Warnung statt einer den Aufrufer abbrechenden Exception.
  5. filter_chargepoints.py löst Verbraucher-Referenzen aus der Prioritätensteuerung defensiv per .get() auf; eine Karteileiche wird geloggt und übersprungen statt calc_current() für alle Ladepunkte abzuschießen.

Bekannte Lücke / Follow-up: inkonsistenter Zustand bei fehlgeschlagenem "add"

Dieser PR macht Fehler bei der Verbraucher-Modul-Erstellung sichtbar und nicht mehr absturzfähig (siehe Problem 1), behebt aber nicht die strukturelle Ursache, warum so ein Zustand überhaupt entstehen kann:

addConsumer() (command.py) legt Hierarchie-Eintrag und Konfiguration synchron an und meldet dem Nutzer sofort "Neues Gerät [...] hinzugefügt" - bevor die eigentliche Modul-Erstellung passiert ist. Diese läuft erst asynchron in subdata.py, wenn das /module-Topic dort verarbeitet wird. Schlägt das fehl, hat der Nutzer bereits eine Erfolgsmeldung gesehen, und es bleibt ein Verbraucher mit Konfiguration, aber ohne funktionierendes Modul zurück - jetzt zumindest mit fault_state/Systemmeldung statt stiller Karteileiche, aber die Karteileiche selbst entsteht weiterhin.

Denkbare nächste Schritte (nicht Teil dieses PRs):

  • addConsumer() erst dann als erfolgreich melden, wenn die Modul-Erstellung bestätigt ist (z. B. über einen Rückkanal/Timeout statt fire-and-forget), oder
  • bei fehlgeschlagener Modul-Erstellung den gerade angelegten Verbraucher automatisch wieder entfernen (Hierarchie, Konfiguration, Prioritätenliste) statt ihn fehlerhaft stehen zu lassen, oder
  • eine Abgleich-/Selbstheilungs-Routine (z. B. beim Start), die Verbraucher ohne funktionierendes Modul automatisch bereinigt.

Das betrifft grundsätzlich denaddConsumer-Ablauf (und potenziell den analogen addDevice/addVehicle-Flow).

@seaspotter
seaspotter requested a review from LKuemmel September 9, 2026 16:08
Comment thread packages/control/counter_all/loadmanagement_prio.py Outdated
…assen

Zwei Bugs in der neu gemergten Verbrauchersteuerung behoben:

1. Modul-Erstellung schlägt fehl -> Regelung crasht dauerhaft
   - setdata.py erkannte openWB/set/consumer/{id}/module/simulation nicht
     als gültiges Topic (Unbekanntes set-Topic-Fehler in jedem Zyklus,
     betrifft jeden Verbraucher mit eingebautem Zähler) und löschte das
     Zähler-Persistenz-Topic dabei sofort wieder.
   - subdata.py interpretierte das gemirrorte .../module/simulation-Topic
     wegen fehlendem $-Anker am /module-Pattern als vollständige
     Modul-Neukonfiguration und scheiterte an KeyError('vendor').
   - Modul-Erstellung in subdata.py schlägt jetzt sichtbar fehl (Log mit
     Verbraucher/vendor/type, fault_state/fault_str, Systemmeldung an die
     UI) statt module auf None zu belassen.
   - loadvars.py überspringt Verbraucher ohne erfolgreich erstelltes Modul
     statt bei jedem Zyklus mit AttributeError abzubrechen.

2. Gelöschter Verbraucher bleibt in Prioritätensteuerung -> Regelung
   berechnet für KEINEN Ladepunkt mehr Strom (discussions#3908, Kommentar
   18370811)
   - remove_loadmanagement_prio_item() verglich die id mit ==; kam sie
     (z.B. aus einem MQTT-Kommando) als str statt int an, wurde das
     Element nicht gefunden, eine IndexError geworfen und dadurch in
     removeConsumer()/removeVehicle() der Rest der Aufräumarbeiten
     (Publish der korrigierten Prioritätenliste) übersprungen - der
     Eintrag blieb dauerhaft im retained Topic, auch über Neustarts
     hinweg.
   - id-Vergleich jetzt tolerant gegen str/int-Mismatch, "nicht gefunden"
     ist eine Warnung statt eine den Aufrufer abbrechende Exception.
   - filter_chargepoints.py löst Verbraucher-Referenzen aus der
     Prioritätensteuerung defensiv per .get() auf; eine Karteileiche wird
     geloggt und übersprungen statt calc_current() für alle Ladepunkte
     abzuschießen.
   - Tests für beide Fälle (flach und in einer Gruppe, sowie str/int-id).
@seaspotter
seaspotter force-pushed the fix-consumer-module-errors branch from 967e9ac to b1e9612 Compare September 11, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants