Streckenberechnung: zuletzt davor statt naechstgelegen, keine Rueckwaertsspruenge (2026.8.31.6)
_fahrt_screenen() holte beide Kilometerstaende mit naechster_wert(), also dem zeitlich naechstgelegenen Wert - gleich ob davor oder danach. Fuer einen Zaehlerstand ist das falsch, und wert_bei()s eigener Docstring sagt es woertlich: der Stand bei Fahrtbeginn ist der zuletzt gemeldete, nicht der naechste, der schon Strecke enthaelt. historienimport.py hat immer wert_bei() benutzt - dieselbe Fahrt wurde also je nach Weg unterschiedlich bewertet, genau das, was die gemeinsame Herkunft von UNPLAUSIBLE_KMH und MINDESTDAUER_S in verlauf.py ausschliesst. Am echten recorder-Verlauf nachgewiesen: fuer den Fahrtbeginn 16:39:42 UTC lieferte naechster_wert() 21325 (der Stand von 16:40:53, also 71 s nach dem Beginn und aus einem anderen Fahrzeug), wert_bei() dagegen 209177. _vollstaendig() prueft ausserdem nach unten. Bisher galt nur > UNPLAUSIBLE_KMH; ein Rueckwaertssprung ergab eine negative Strecke und wurde als "vollstaendig" gespeichert. historienimport.py verlangt odo_end >= odo_start seit jeher. Die Pruefung sitzt an der Strecke, nicht an der Geschwindigkeit: durchschnitt_kmh() liefert bei Dauer 0 None, eine Fahrt mit null Sekunden kaeme sonst vorbei. Dem Fahrzeugwechsel des Dongles waehrend der Testphase steht das nicht im Weg: verglichen werden Anfang und Ende derselben Fahrt, und jedes Fahrzeug zaehlt innerhalb seiner eigenen Fahrt aufwaerts. In allen 23 gespeicherten Fahrten haette die Pruefung nie ausgeloest. Greift sie doch, werden beide Werte verworfen und die Fahrt bleibt "offen" - der naechste Screening-Lauf versucht es erneut. Verifiziert gegen die echte Aufzeichnung in audi_ha_test: sieben Faelle, darunter drei Regressionen (normale Fahrt, unmoegliches Tempo, echte 0-km-Fahrt bei stillstehendem Zaehler). py_compile sauber, 2026.8.31.6 sauber gestartet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.8.31.5",
|
||||
"version": "2026.8.31.6",
|
||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||
"codeowners": ["@paul"],
|
||||
|
||||
@@ -4,8 +4,17 @@ Endkilometerstand vervollständigen (SPECIFICATION.md §7.2).
|
||||
Der Kilometerstand kommt laut Datenquelle nicht sicher mit Fahrtende, sondern
|
||||
teils erst mit Beginn oder während der nächsten Fahrt. Statt auf einen festen
|
||||
Zeitpunkt zu warten, wird deshalb der aufgezeichnete Verlauf des
|
||||
Kilometerstand-Sensors nach dem Wert durchsucht, dessen Zeitstempel am
|
||||
nächsten am Fahrtbeginn bzw. -ende liegt.
|
||||
Kilometerstand-Sensors gelesen und daraus der Stand zu Fahrtbeginn bzw. -ende
|
||||
bestimmt - mit wert_bei(), also dem zuletzt DAVOR gemeldeten Wert.
|
||||
|
||||
Bis zum 31.08.2026 stand hier naechster_wert(): der zeitlich nächstgelegene
|
||||
Wert, gleich ob davor oder danach. Für einen Zählerstand ist das falsch, und
|
||||
wert_bei()s eigener Docstring sagt es wörtlich - der Kilometerstand bei
|
||||
Fahrtbeginn ist der zuletzt gemeldete, nicht der nächste, der schon Strecke
|
||||
enthält. Der Rückblick (historienimport.py) hat immer wert_bei() benutzt;
|
||||
dieselbe Fahrt wurde also je nach Weg unterschiedlich bewertet - genau das,
|
||||
was die gemeinsame Herkunft von UNPLAUSIBLE_KMH und MINDESTDAUER_S in
|
||||
verlauf.py ausschließt.
|
||||
|
||||
Läuft nach jedem Fahrtende und zusätzlich bei jeder Änderung des
|
||||
Kilometerstand-Sensors - unabhängig vom Fahrtende-Ereignis selbst, eben weil
|
||||
@@ -30,6 +39,7 @@ from .verlauf import (
|
||||
route_aus_verlauf,
|
||||
verbrauch_aus_literstaenden,
|
||||
verlauf_lesen,
|
||||
wert_bei,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -67,6 +77,31 @@ def _vollstaendig(aenderungen: dict, fahrt: dict) -> dict:
|
||||
einen unmöglichen Wert dauerhaft zu speichern."""
|
||||
if fahrt.get("odo_start") is not None and fahrt.get("odo_end") is not None:
|
||||
distanz = round(fahrt["odo_end"] - fahrt["odo_start"], 1)
|
||||
if distanz < 0:
|
||||
# Ein Zähler läuft nicht rückwärts - der Wert stammt also nicht
|
||||
# aus derselben Quelle wie sein Gegenstück. Während der Testphase
|
||||
# wandert der Dongle zwischen Fahrzeugen; am 31.08.2026 meldete der
|
||||
# CAN-Kilometerstand deshalb 21325 zwischen 209177 und 209267.
|
||||
#
|
||||
# Die Prüfung vergleicht Anfang und Ende DERSELBEN Fahrt und steht
|
||||
# dem Fahrzeugwechsel nicht im Weg: jedes Fahrzeug zählt innerhalb
|
||||
# seiner eigenen Fahrt aufwärts. Sie greift nur, wenn die beiden
|
||||
# Enden aus verschiedenen Fahrzeugen stammen.
|
||||
#
|
||||
# Warum an der Strecke und nicht an der Geschwindigkeit: die
|
||||
# Plausibilitätsgrenze darunter prüft nur nach oben, und
|
||||
# durchschnitt_kmh() liefert bei Dauer 0 None - eine Fahrt mit null
|
||||
# Sekunden Dauer käme also an einer Geschwindigkeitsprüfung vorbei.
|
||||
#
|
||||
# historienimport.py verlangt seit jeher odo_end >= odo_start.
|
||||
_LOGGER.warning(
|
||||
"Fahrt %s: Kilometerstand verworfen - Rückwärtssprung (%s -> %s, "
|
||||
"%s km), vermutlich ein Fahrzeugwechsel des Geräts.",
|
||||
fahrt.get("trip_id"), fahrt["odo_start"], fahrt["odo_end"], distanz,
|
||||
)
|
||||
aenderungen["odo_start"] = None
|
||||
aenderungen["odo_end"] = None
|
||||
return aenderungen
|
||||
geschwindigkeit = durchschnitt_kmh(distanz, fahrt.get("duration_s"))
|
||||
if geschwindigkeit is not None and geschwindigkeit > UNPLAUSIBLE_KMH:
|
||||
_LOGGER.warning(
|
||||
@@ -159,14 +194,18 @@ async def _fahrt_screenen(k: Koordinator, km_sensor: str, fahrt: dict) -> None:
|
||||
if not punkte:
|
||||
return
|
||||
|
||||
# wert_bei() statt naechster_wert(): der zuletzt DAVOR gemeldete Stand, nicht
|
||||
# der zeitlich nächstgelegene. Siehe Modulkopf - für einen Zähler ist das die
|
||||
# richtige Wahl, und der Rückblick in historienimport.py macht es genauso.
|
||||
# Steht davor nichts, nimmt wert_bei() den ersten Wert danach.
|
||||
geaendert = False
|
||||
if fahrt.get("odo_end") is None:
|
||||
wert = naechster_wert(ende, punkte)
|
||||
wert = wert_bei(punkte, ende)
|
||||
if wert is not None:
|
||||
fahrt["odo_end"] = wert
|
||||
geaendert = True
|
||||
if fahrt.get("odo_start") is None:
|
||||
wert = naechster_wert(start, punkte)
|
||||
wert = wert_bei(punkte, start)
|
||||
if wert is not None:
|
||||
fahrt["odo_start"] = wert
|
||||
geaendert = True
|
||||
|
||||
Reference in New Issue
Block a user