Fix event loop bloqué + /status non-bloquant
- 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 <noreply@anthropic.com>
This commit is contained in:
@ -34,21 +34,15 @@ async def command(req: CommandRequest):
|
|||||||
|
|
||||||
@app.get("/status")
|
@app.get("/status")
|
||||||
async def status():
|
async def status():
|
||||||
await wireclaw_core.refresh_position(wireclaw_core.drone)
|
# Lecture pure depuis les caches mis à jour par les tâches de fond —
|
||||||
async for bat in wireclaw_core.drone.telemetry.battery():
|
# aucun appel bloquant, réponse instantanée
|
||||||
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
|
|
||||||
return {
|
return {
|
||||||
"lat": wireclaw_core.current_pos["lat"],
|
"lat": wireclaw_core.current_pos["lat"],
|
||||||
"lon": wireclaw_core.current_pos["lon"],
|
"lon": wireclaw_core.current_pos["lon"],
|
||||||
"alt": round(wireclaw_core.current_pos["alt"], 2),
|
"alt": round(wireclaw_core.current_pos["alt"], 2),
|
||||||
"abs_alt": round(wireclaw_core.current_pos["abs_alt"], 2),
|
"abs_alt": round(wireclaw_core.current_pos["abs_alt"], 2),
|
||||||
"battery": round(pct, 1),
|
"battery": round(wireclaw_core.telemetry_cache["battery"], 1),
|
||||||
"mode": mode,
|
"mode": wireclaw_core.telemetry_cache["mode"],
|
||||||
"armed": wireclaw_core.drone_armed,
|
"armed": wireclaw_core.drone_armed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ Nord = lat+, Sud = lat-, Est = lon+, Ouest = lon-
|
|||||||
|
|
||||||
drone_armed = False
|
drone_armed = False
|
||||||
current_pos = {"lat": BASE_LAT, "lon": BASE_LON, "alt": 0, "abs_alt": 584}
|
current_pos = {"lat": BASE_LAT, "lon": BASE_LON, "alt": 0, "abs_alt": 584}
|
||||||
|
telemetry_cache = {"battery": -1.0, "mode": "UNKNOWN"}
|
||||||
mav = None
|
mav = None
|
||||||
drone = None
|
drone = None
|
||||||
|
|
||||||
@ -61,6 +62,19 @@ async def monitor_armed(drone):
|
|||||||
async for is_armed in drone.telemetry.armed():
|
async for is_armed in drone.telemetry.armed():
|
||||||
drone_armed = is_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):
|
async def interpret_command(text):
|
||||||
response = client.models.generate_content(
|
response = client.models.generate_content(
|
||||||
model=GEMINI_MODEL,
|
model=GEMINI_MODEL,
|
||||||
@ -91,18 +105,11 @@ async def execute_command(drone, cmd):
|
|||||||
|
|
||||||
elif action == "status":
|
elif action == "status":
|
||||||
await refresh_position(drone)
|
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"
|
return (f"\nPosition : {current_pos['lat']:.6f}, {current_pos['lon']:.6f}\n"
|
||||||
f"Altitude : {current_pos['alt']:.1f}m (AGL)\n"
|
f"Altitude : {current_pos['alt']:.1f}m (AGL)\n"
|
||||||
f"Abs alt : {current_pos['abs_alt']:.1f}m\n"
|
f"Abs alt : {current_pos['abs_alt']:.1f}m\n"
|
||||||
f"Batterie : {pct:.0f}%\n"
|
f"Batterie : {telemetry_cache['battery']:.0f}%\n"
|
||||||
f"Mode : {mode}\n"
|
f"Mode : {telemetry_cache['mode']}\n"
|
||||||
f"Arme : {drone_armed}")
|
f"Arme : {drone_armed}")
|
||||||
|
|
||||||
elif action == "takeoff":
|
elif action == "takeoff":
|
||||||
@ -199,9 +206,11 @@ async def execute_command(drone, cmd):
|
|||||||
return "Action inconnue : " + action
|
return "Action inconnue : " + action
|
||||||
|
|
||||||
async def init():
|
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
|
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()
|
drone = System()
|
||||||
await drone.connect(system_address=MAVLINK_ADDRESS)
|
await drone.connect(system_address=MAVLINK_ADDRESS)
|
||||||
print("Connexion MAVSDK...")
|
print("Connexion MAVSDK...")
|
||||||
@ -213,6 +222,7 @@ async def init():
|
|||||||
print(f"Etat initial : {'arme' if drone_armed else 'desarme'}")
|
print(f"Etat initial : {'arme' if drone_armed else 'desarme'}")
|
||||||
break
|
break
|
||||||
asyncio.ensure_future(monitor_armed(drone))
|
asyncio.ensure_future(monitor_armed(drone))
|
||||||
|
asyncio.ensure_future(monitor_telemetry(drone))
|
||||||
break
|
break
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
|||||||
Reference in New Issue
Block a user