From 6024655a50f8e116f75a49f23db4d3b66133edc3 Mon Sep 17 00:00:00 2001 From: nicoboy Date: Mon, 1 Jun 2026 14:22:02 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20event=20loop=20bloqu=C3=A9=20+=20/status?= =?UTF-8?q?=20non-bloquant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - init_pymavlink() passe en run_in_executor : ne bloque plus l'event loop asyncio pendant le démarrage de uvicorn - Ajout monitor_telemetry() : batterie et mode mis à jour en continu en cache (même logique que monitor_armed) - /status et action status lisent telemetry_cache directement, plus aucun async for bloquant dans les route handlers Co-Authored-By: Claude Sonnet 4.6 --- web_server.py | 14 ++++---------- wireclaw_core.py | 32 +++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/web_server.py b/web_server.py index 092edc1..7354180 100644 --- a/web_server.py +++ b/web_server.py @@ -34,21 +34,15 @@ async def command(req: CommandRequest): @app.get("/status") async def status(): - await wireclaw_core.refresh_position(wireclaw_core.drone) - async for bat in wireclaw_core.drone.telemetry.battery(): - raw = bat.remaining_percent - pct = abs(raw) * 100 if abs(raw) <= 1 else abs(raw) - break - async for fm in wireclaw_core.drone.telemetry.flight_mode(): - mode = str(fm) - break + # Lecture pure depuis les caches mis à jour par les tâches de fond — + # aucun appel bloquant, réponse instantanée return { "lat": wireclaw_core.current_pos["lat"], "lon": wireclaw_core.current_pos["lon"], "alt": round(wireclaw_core.current_pos["alt"], 2), "abs_alt": round(wireclaw_core.current_pos["abs_alt"], 2), - "battery": round(pct, 1), - "mode": mode, + "battery": round(wireclaw_core.telemetry_cache["battery"], 1), + "mode": wireclaw_core.telemetry_cache["mode"], "armed": wireclaw_core.drone_armed, } diff --git a/wireclaw_core.py b/wireclaw_core.py index d6d862f..0f4a0c0 100644 --- a/wireclaw_core.py +++ b/wireclaw_core.py @@ -32,6 +32,7 @@ Nord = lat+, Sud = lat-, Est = lon+, Ouest = lon- drone_armed = False current_pos = {"lat": BASE_LAT, "lon": BASE_LON, "alt": 0, "abs_alt": 584} +telemetry_cache = {"battery": -1.0, "mode": "UNKNOWN"} mav = None drone = None @@ -61,6 +62,19 @@ async def monitor_armed(drone): async for is_armed in drone.telemetry.armed(): drone_armed = is_armed +async def monitor_telemetry(drone): + """Met à jour en continu batterie et mode de vol dans telemetry_cache.""" + async def watch_battery(): + async for bat in drone.telemetry.battery(): + raw = bat.remaining_percent + telemetry_cache["battery"] = abs(raw) * 100 if abs(raw) <= 1 else abs(raw) + + async def watch_mode(): + async for fm in drone.telemetry.flight_mode(): + telemetry_cache["mode"] = str(fm) + + await asyncio.gather(watch_battery(), watch_mode()) + async def interpret_command(text): response = client.models.generate_content( model=GEMINI_MODEL, @@ -91,18 +105,11 @@ async def execute_command(drone, cmd): elif action == "status": await refresh_position(drone) - async for bat in drone.telemetry.battery(): - raw = bat.remaining_percent - pct = abs(raw) * 100 if abs(raw) <= 1 else abs(raw) - break - async for fm in drone.telemetry.flight_mode(): - mode = str(fm) - break return (f"\nPosition : {current_pos['lat']:.6f}, {current_pos['lon']:.6f}\n" f"Altitude : {current_pos['alt']:.1f}m (AGL)\n" f"Abs alt : {current_pos['abs_alt']:.1f}m\n" - f"Batterie : {pct:.0f}%\n" - f"Mode : {mode}\n" + f"Batterie : {telemetry_cache['battery']:.0f}%\n" + f"Mode : {telemetry_cache['mode']}\n" f"Arme : {drone_armed}") elif action == "takeoff": @@ -199,9 +206,11 @@ async def execute_command(drone, cmd): return "Action inconnue : " + action async def init(): - """Initialise pymavlink, MAVSDK et démarre monitor_armed en tâche de fond.""" + """Initialise pymavlink, MAVSDK et démarre les tâches de fond de télémétrie.""" global drone, drone_armed - init_pymavlink() + # init_pymavlink est synchrone et bloquant — run_in_executor évite de geler l'event loop + loop = asyncio.get_event_loop() + await loop.run_in_executor(None, init_pymavlink) drone = System() await drone.connect(system_address=MAVLINK_ADDRESS) print("Connexion MAVSDK...") @@ -213,6 +222,7 @@ async def init(): print(f"Etat initial : {'arme' if drone_armed else 'desarme'}") break asyncio.ensure_future(monitor_armed(drone)) + asyncio.ensure_future(monitor_telemetry(drone)) break async def main():