PV/Speicher/Verbraucher: nach 60s Fehler auf 0 setzen - #4002
seaspotter wants to merge 8 commits into
Conversation
|
Fixed auch #3990 |
|
Bei den Ladepunkten wird direkt in der Ladepunkt-Klasse bei Ablaufen des Fehler-Timeouts die Leistung auf 0W gesetzt. Nicht erst in der Chargepoint-Klasse, die die Logik für die Regelung abbildet. Ich finde, dass Zurücksetzen im Fehlerfall sollte noch innerhalb der Aufrufe von loadvars erfolgen. Allerdings wird bei den Komponenten die Werte im Fehlerzustand nicht mehr geschrieben, bei den Ladepunkten aber schon und bei |
Danke für den Hinweis Lena. Wenn ich dich richtig verstehe: du hättest das Zurücksetzen auf 0 im Fehlerfall lieber innerhalb der loadvars-Kette (also auf Modul-/Komponenten-Ebene, so wie es für Geräte über MultiComponentUpdateContext.error_handler im Prinzip schon vorgesehen ist), statt wie jetzt in control/ - richtig? Falls ja: das würde heißen, die Logik aus pv_all.py/bat_all.py/consumer/consumer.py wieder rauszunehmen und stattdessen in die jeweiligen ConfigurableDevice/ConfigurableConsumer-Klassen zu verschieben - und dabei gleich den Bug mitzunehmen, dass ConfigurableConsumer.error_handler() aktuell nirgends aufgerufen wird, Verbraucher also bislang gar kein Reset im Fehlerfall bekommen. Ist das der Ansatz, den du dir vorstellst, oder meinst du etwas anderes? Nur als weiteres Beispiel, warum sich das schwer von selbst ableiten lässt: pv.py/pv_all.py und bat.py/bat_all.py liegen direkt unter control/, consumer.py aber schon in einem eigenen control/consumer/-Unterordner - es gibt also nicht mal auf Ordnerebene eine einheitliche Konvention, an der ich mich orientieren könnte. Und dazu noch ein offenes Wort, auch wenn es unangenehm ist: bei diesem PR und auch schon bei #3899 (dort jetzt die dritte Runde) stehe ich jeweils lange ohne echte Rückmeldung da, wo ein Fix architektonisch hinsoll, bis das erst im Review nach bereits geleisteter Arbeit kommt. Das kostet mich jedes Mal viel Zeit, die eine kurze Einschätzung vorher gespart hätte. Wenn du schon eine klare Vorstellung hast, wo etwas strukturell hingehört, wäre es mir sehr geholfen, die vorher statt hinterher zu bekommen - sonst überlege ich mir, solche architektonischen Entscheidungen lieber dir zu überlassen, statt mehrere Runden Arbeit zu investieren, die dann erneut wieder doch nicht passen. |
Ja, da muss es hin. |
…chnen Analog zum bestehenden Mechanismus beim Zähler (MAX_EVU_ERROR_DURATION): wenn eine PV-, Speicher- oder Verbraucher-Komponente dauerhaft im Fehlerzustand ist, wird sie nach 60s nicht mehr mit ihrem letzten bekannten Leistungswert in die jeweilige Summe eingerechnet, sondern mit 0W. Der eigene get.power-Wert der Komponente bleibt unverändert (gleiches Verhalten wie beim Zähler) - nur die Summenbildung in PvAll/BatAll/AllConsumers sowie Counter._set_power_left berücksichtigt den Fehlerfall.
…ler nicht mehr mit altem Wert rechnen Vereinheitlicht den Fehlerfall-Mechanismus über Counter/Chargepoint/PV/Bat/Consumer: gemeinsame COMPONENT_ERROR_DURATION-Konstante und error_duration_exceeded()-Prüfung. PV/Bat setzen get.power nach Ablauf der Gnadenfrist direkt auf 0 (wie Counter/Chargepoint bereits), pro Komponente einzeln. Consumer.update() ist jetzt die einzige Stelle, die den Fehlerfall abbildet - behebt eine ~1-Zyklus-Verzögerung zwischen Counter._set_power_left() und ConsumerAll.get_consumer_sum(). Zusätzliche Gates verhindern, dass die aktive Speichersteuerung (BatAll._set_bat_power_active_control(), inkl. Ausschluss fehlerhafter Speicher aus der Kapazitäts-Summe) bzw. die Verbraucher-Ansteuerung (Consumer.get_parameter()) noch auf Basis veralteter Werte weiterarbeiten.
…h nie persistiert setdata.py kannte /set/error_timer nur für counter, wurde für pv/bat/consumer per __unknown_topic verworfen. subdata.py routete /pv/<id>/set/ zusätzlich gar nicht erst in Pv.data.set. Da control/data.py jeden Zyklus frisch aus SubData kopiert, wurde error_timer dadurch bei jedem Zyklus auf None zurückgesetzt - der Fehlerzustand konnte nie 60s andauern. Live auf Testsystem gefunden (error_timer stieg bei jedem Print statt konstant zu bleiben).
…UI zurückgemeldet
Anders als bei PV/Bat fehlte bei Consumer.Get.power die metadata={"topic": "get/power"}.
changed_values_handler publiziert Feldänderungen nur für Felder mit Topic-Metadaten - der
vom Fehlerfall-Mechanismus berechnete Wert (0 nach 60s) blieb dadurch rein im Python-Prozess
hängen und erreichte nie den Broker bzw. die Status-Karte in openwb-ui-settings
(ConsumerCard.vue liest direkt von openWB/consumer/<id>/get/power). Die Summe (Alle
Verbraucher) war davon nicht betroffen, da consumer_all.py's power-Feld die Metadaten
bereits hatte.
…R_DURATION nutzen War nur noch ein Alias auf COMPONENT_ERROR_DURATION mit genau einer Verwendungsstelle - konsequent zu Ende geführt, damit über alle Module hinweg dieselbe Konstante sichtbar ist.
6dc6138 to
792f706
Compare
…lieren Analog zu RCT (openWB#3958): ein Fehler bei nur einer Komponente (falsches Register/Endpoint) markiert nicht mehr fälschlich die anderen als defekt.
f578eda to
ebe66ce
Compare
FaultState bekommt einen on_sustained_error-Callback, der aus store_error() heraus feuert - also im Lese-Zyklus (SingleComponentUpdateContext/ MultiComponentUpdateContext, ConfigurableDevice.update()), nicht erst beim späteren Publizieren. Der Lese-Zyklus läuft unconditioned jeden Durchlauf, unabhängig davon, ob eine Komponente bereits als fehlerhaft bekannt ist - loadvars.py überspringt genau das für bereits fehlerhafte Komponenten beim Publizieren (get_finished_component_obj_by_id()), was die Nullung sonst nie erreicht hätte. Verdrahtet einmalig, zentral: ConfigurableDevice.add_component() für PV/Bat, ConfigurableConsumer.__init__ für Verbraucher - pro Komponente, kein Gerätemodul angefasst. Dabei mitbehoben: - ConfigurableConsumer.update() hat Exceptions verschluckt, bevor error_handler() sie je sah (reraise=False) - Verbraucher bekamen dadurch nie dessen Fehlerbehandlung. - PurgeInverterState.update() korrigierte bei Hybrid-WR den zuletzt gesetzten Wert in-place und lief auch ohne vorheriges set() bei einem Lesefehler jeden Zyklus erneut - die Speicherleistung wurde dadurch bei jedem Fehler erneut vom Wechselrichter-Wert abgezogen. update() korrigiert jetzt eine Kopie. - Verbraucher mit externem Zähler (extra_meter) richten die Nullung nach dem Fehlerzustand des Zählers selbst, nicht des Verbrauchers (dessen eigener Fehler ohnehin nur als Statusmeldung relevant ist, da die Leistung komplett vom Zähler kommt). control/: effective_power()/EffectivePowerResult entfernt - pv_all.py/ bat_all.py/consumer.py vertrauen jetzt direkt auf get.power. Aktive Speichersteuerung und Verbraucher-Schaltbefehl-Gate bleiben in control/ (eigene Entscheidung, keine Werte mehr aktiv zu steuern), dafür neu: tick_error_timer().
zero_power_on_sustained_error() ging bisher von einem bereits gelesenen state aus - eine Komponente, die seit dem Programmstart nie erfolgreich gelesen wurde, hatte nichts zu nullen und blieb stumm. Ein davor über MQTT retained alter Wert wäre dadurch nie überschrieben worden. Publiziert jetzt in diesem Fall einen frischen Nullwert.
PR ist komplett überarbeitet - die Nullung läuft jetzt direkt im Lese-Zyklus der Module, nicht mehr in control/, so wie besprochen. Zwei Dinge zu Fehlerzuständen bei Verbrauchern mit externem Zähler (extra_meter) sind mir beim Testen aufgefallen, die ich nicht selbst entscheiden wollte:
Ist aus meiner Sicht eine Experience-Entscheidung wie die Benachrichtigungsfrage vorher. Baust du das gern selbst ein, falls du eine Richtung im Kopf hast? Ich finds so etwas unschön, oder zumindest eine Warnung, kein Fehler in beiden Komponenten dann? Deine Entscheidung. |
Edit 03.10.2026:
Follow-up zu #3959 (und #3951/#3931). PV/Speicher/Verbraucher verhalten sich jetzt wie Zähler/Ladepunkt: nach
COMPONENT_ERROR_DURATION(60s) andauerndem Fehler wird nicht mehr mit dem letzten bekannten Wert weitergerechnet bzw. weitergesteuert.Mechanismus:
FaultStatebekommt einenon_sustained_error-Callback, der ausstore_error()heraus feuert - direkt im Lese-Zyklus (SingleComponentUpdateContext/MultiComponentUpdateContext, innerhalb vonConfigurableDevice.update()), nicht erst beim späteren Publizieren. Verdrahtet einmalig und zentral inConfigurableDevice.add_component()bzw.ConfigurableConsumer.__init__- pro Komponente, kein Gerätemodul einzeln angefasst.Grund für den Lese- statt Publizier-Zyklus:
get_finished_component_obj_by_id()(aus #3958) überspringt bereits fehlerhafte Komponenten beim erneuten Publizieren - die Nullung hätte das sonst nie erreicht. Der Lese-Zyklus läuft davon unabhängig jeden Durchlauf.error_duration_exceeded()-Prüfung statt vier unabhängig benannter Varianten (Counter, Chargepoint, PV, Bat, Consumer nutzen dieselbe Logik/Wortwahl)BatAll._set_bat_power_active_control()) und Verbraucher-Schaltbefehl-Gate (Consumer.get_parameter()) bleiben bewusst incontrol/- eigene Entscheidung, keine Werte mehr aktiv zu steuern, getrennt von der Frage was gemeldet wirdDabei gefunden und mitbehoben:
ConfigurableConsumer.error_handler()war nie verdrahtet (wie besprochen)PurgeInverterState.update()korrigierte bei Hybrid-WR den letzten Wert in-place und lief auch ohne neuen Read jeden Zyklus erneut - dieselbe Fehlerklasse wie-PR Wechselrichter: Hybrid-Korrektur nicht erneut anwenden, wenn kein neuer Wert gesetzt wurde #4048, unabhängig davon gefunden und behobenGetestet