fix(trigger): near() fired bei Auto-Vorbeifahrten verpasst — Loop schneller + event-getrieben
Stefan ist mehrmals an einem 300m-near()-Watcher (DRK Kreyenbrueck)
vorbeigefahren, kein Fire. Ursache: Background-Loop tickte alle 30s,
Auto-Durchfahrt durch 600m-Durchmesser-Radius dauert bei 50-120 km/h
nur 18-43 Sekunden — der Tick konnte komplett dazwischen liegen.
Drei Fixes (A + B aus Stefans Vorschlag):
A1. Background-Loop-Frequenz: TICK_SEC 30 → 8.
Garantiert mind. 2 Checks auch bei 120 km/h durch 300m. Loop ist
billig (paar Dateilesungen + AST-Eval), Brain merkt das nicht.
A2. near() bekommt Age-Schutz (watcher.py NEAR_MAX_AGE_SEC=300):
Wenn location_age_sec > 5 min, gilt die Position als unbekannt
und near() liefert False. Verhindert Phantom-Fires wenn Tracking
aus ist oder Mobilfunk weg war — vorher haette der letzte
bekannte Wert weiter ausgewertet werden koennen.
B. Event-getriebener Tick:
- background.py: tick_now()-Funktion + Module-Slot fuer
agent_factory damit man von ausserhalb des Lifespan-Pfads
einen Tick triggern kann
- main.py: POST /triggers/check-now Endpoint ruft tick_now()
- bridge: _persist_location feuert nach jedem Save ein fire-and-
forget POST /triggers/check-now (run_in_executor, timeout 8s,
blockt nichts wenn Brain stockt)
Damit fires near() sofort wenn die App ein location_update schickt —
Polling ist nur noch der Fallback fuer Watcher OHNE GPS-Bezug
(disk_free, hour_of_day etc.) und als Sicherheits-Tick falls
location_update mal ausfaellt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,12 @@ import watcher as watcher_mod
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TICK_SEC = 30
|
||||
# Polling-Frequenz des Background-Loops. Vorher 30s → Auto-Vorbeifahrt
|
||||
# durch einen 300m-Radius bei >50 km/h konnte zwischen zwei Ticks komplett
|
||||
# verpasst werden. Mit 8s ist auch eine 18-Sekunden-Durchfahrt (120 km/h
|
||||
# durch 300m) garantiert mind. einmal getroffen. Der Loop ist billig
|
||||
# (paar Dateilesungen + AST-Eval), das macht Brain nicht warm.
|
||||
TICK_SEC = 8
|
||||
BRIDGE_URL = os.environ.get("BRIDGE_URL", "http://aria-bridge:8090")
|
||||
|
||||
|
||||
@@ -195,8 +200,31 @@ async def _tick(agent_factory) -> None:
|
||||
logger.warning("Trigger-Check %s: %s", trigger.get("name"), e)
|
||||
|
||||
|
||||
# Module-Level-Slot fuer die agent_factory damit on-demand-Ticks (von
|
||||
# z.B. POST /triggers/check-now) Zugang haben ohne durch den ganzen
|
||||
# Lifespan-Pfad geschleust zu werden.
|
||||
_AGENT_FACTORY = None
|
||||
|
||||
|
||||
async def tick_now() -> dict:
|
||||
"""Sofortiger Trigger-Check — nicht warten auf den naechsten Loop-Tick.
|
||||
Wird genutzt wenn ein neues GPS-Update reinkommt: Bridge ruft das nach
|
||||
_persist_location, damit Watcher mit near() den frischen Wert sofort
|
||||
sehen statt bis zu TICK_SEC Sekunden zu warten."""
|
||||
if _AGENT_FACTORY is None:
|
||||
return {"ok": False, "error": "Background-Loop noch nicht gestartet"}
|
||||
try:
|
||||
await _tick(_AGENT_FACTORY)
|
||||
return {"ok": True}
|
||||
except Exception as exc:
|
||||
logger.exception("tick_now: %s", exc)
|
||||
return {"ok": False, "error": str(exc)}
|
||||
|
||||
|
||||
async def run_loop(agent_factory) -> None:
|
||||
"""Endlosschleife — wird vom main lifespan gestartet + gestoppt."""
|
||||
global _AGENT_FACTORY
|
||||
_AGENT_FACTORY = agent_factory
|
||||
logger.info("Trigger-Loop gestartet (TICK_SEC=%d)", TICK_SEC)
|
||||
while True:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user