fix: EVSE-Ruecklesen vor Phasenumschaltung#3326
fix: EVSE-Ruecklesen vor Phasenumschaltung#3326s0170071 wants to merge 2 commits intoopenWB:masterfrom
Conversation
Ersetzt den blinden 5s-Sleep in perform_phase_switch durch eine aktive Ruecklesung des EVSE-Registers (bis zu 20x alle 0,5s). Die Phasen- umschaltung wird abgebrochen wenn die EVSE nach 10s keinen Strom von 0 A bestaetigt. reraise=True verhindert dass ein fehlgeschlagener set_current(0) stillschweigend geschluckt wird.
9843260 to
c9ce41a
Compare
| log.error("CP%d: EVSE did not reach 0 A within 10s — aborting phase switch.", | ||
| self.local_charge_point_num) | ||
| return |
There was a problem hiding this comment.
| log.error("CP%d: EVSE did not reach 0 A within 10s — aborting phase switch.", | |
| self.local_charge_point_num) | |
| return | |
| raise Exception("Ladung konnte nicht gestoppt werden - Phasenumschaltung abgebrochen.") |
Mit einer Exception bekommt der Nutzer auch im UI eine Rückmeldung, warum die Umschaltung nicht geklappt hat.
There was a problem hiding this comment.
Pull request overview
This PR hardens the internal chargepoint phase-switch procedure to avoid switching relays under load by actively verifying that the EVSE has accepted a 0 A setpoint and by ensuring write failures are not silently swallowed.
Changes:
- Enable exception propagation (
reraise=True) during EVSE current write before phase switching. - Replace fixed sleep with polling of EVSE state (up to 20 attempts) and abort phase switching if 0 A isn’t confirmed.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for _ in range(20): # poll up to 10s (20 × 0.5s) for EVSE to confirm 0 A | ||
| _, _, evse_current = evse.get_plug_charge_state() | ||
| if evse_current == 0: | ||
| break | ||
| time.sleep(0.5) |
There was a problem hiding this comment.
The "poll up to 10s" timeout isn’t actually bounded to ~10s: each loop iteration also includes EVSE.get_plug_charge_state() (0.1s sleep) and evse.set_current() (0.1s sleep) in addition to the 0.5s sleep, so the worst-case runtime is noticeably >10s (plus Modbus latency). Consider implementing the timeout using a monotonic deadline (e.g., loop until time.monotonic() >= start+10) or adjusting the sleep/iteration count so the maximum wall time matches the intended 10s.
| for _ in range(20): # poll up to 10s (20 × 0.5s) for EVSE to confirm 0 A | |
| _, _, evse_current = evse.get_plug_charge_state() | |
| if evse_current == 0: | |
| break | |
| time.sleep(0.5) | |
| deadline = time.monotonic() + 10 | |
| while time.monotonic() < deadline: # poll up to 10s for EVSE to confirm 0 A | |
| _, _, evse_current = evse.get_plug_charge_state() | |
| if evse_current == 0: | |
| break | |
| remaining = deadline - time.monotonic() | |
| if remaining <= 0: | |
| break | |
| time.sleep(min(0.5, remaining)) |
Problem
Beim Phasenwechsel wird aktuell
set_current(0)an die EVSE gesendet und dann blind 5 Sekunden gewartet, bevor die Relais geschaltet werden. Wenn der EVSE-Schreibvorgang fehlschlägt (Modbus-Timeout, Bus-Konflikt,evse_client is None), wird die Exception vonSingleComponentUpdateContextstillschweigend geschluckt und die Relais werden unter Last geschaltet.Folgen: Lichtbogenschäden an den Relaiskontakten (nur für stromloses Schalten ausgelegt), mögliches Verschweißen der Kontakte, im schlimmsten Fall Brandgefahr bei 32 A.
Ladeabruch da das Fahrzeug einen Fehler erkennt.
Lösung
reraise=TrueaufSingleComponentUpdateContext: Ein fehlgeschlagenerset_current(0)-Aufruf wird nicht mehr geschluckt, sondern als Exception weitergegeben — die Relais werden nicht geschaltet.set_current(0)erneut gesendet.log.errorabgebrochen. Die Relais werden nicht betätigt.Geänderte Datei
packages/modules/internal_chargepoint_handler/chargepoint_module.py—perform_phase_switch()Test
set_current(0): Exception wird geworfen → Relais bleiben unangetastet