From bcda9cf33405125442c68589746b61598c4236df Mon Sep 17 00:00:00 2001 From: nicoboy Date: Mon, 1 Jun 2026 13:54:54 +0200 Subject: [PATCH] Ajout serveur web FastAPI sur port 8765 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - wireclaw_core : expose drone comme global, extrait init() depuis main() pour permettre l'import par web_server.py ; corrige aussi le bug drone_armed manquant global dans init - web_server.py : FastAPI avec process(text) générique (interpret_command → execute_command), routes GET / POST /command GET /status, page HTML sobre avec statut temps réel (2s) et historique des commandes Co-Authored-By: Claude Sonnet 4.6 --- web_server.py | 296 +++++++++++++++++++++++++++++++++++++++++++++++ wireclaw_core.py | 7 +- 2 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 web_server.py diff --git a/web_server.py b/web_server.py new file mode 100644 index 0000000..092edc1 --- /dev/null +++ b/web_server.py @@ -0,0 +1,296 @@ +import asyncio +from contextlib import asynccontextmanager +from fastapi import FastAPI +from fastapi.responses import HTMLResponse, JSONResponse +from pydantic import BaseModel + +import wireclaw_core + + +@asynccontextmanager +async def lifespan(app: FastAPI): + await wireclaw_core.init() + yield + + +app = FastAPI(lifespan=lifespan) + + +async def process(text: str) -> str: + """Point d'entrée générique : texte libre → commande interprétée → exécutée.""" + cmd = await wireclaw_core.interpret_command(text) + return await wireclaw_core.execute_command(wireclaw_core.drone, cmd) + + +class CommandRequest(BaseModel): + text: str + + +@app.post("/command") +async def command(req: CommandRequest): + response = await process(req.text) + return {"response": response} + + +@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 + 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, + "armed": wireclaw_core.drone_armed, + } + + +@app.get("/") +async def index(): + return HTMLResponse(HTML) + + +HTML = """ + + + + + WireClaw + + + +

WIRECLAW

+ +
+
+
Latitude
+
+
+
+
Longitude
+
+
+
+
Altitude AGL
+
+
+
+
Alt MSL
+
+
+
+
Batterie
+
+
+
+
Mode
+
+
+
+
Arme
+
+
+
+ +
+
+ + +
+
+ + + + + +
+
+ +
+ + + + +""" diff --git a/wireclaw_core.py b/wireclaw_core.py index dd88f22..d6d862f 100644 --- a/wireclaw_core.py +++ b/wireclaw_core.py @@ -33,6 +33,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} mav = None +drone = None def init_pymavlink(): global mav @@ -197,7 +198,9 @@ async def execute_command(drone, cmd): return "Action inconnue : " + action -async def main(): +async def init(): + """Initialise pymavlink, MAVSDK et démarre monitor_armed en tâche de fond.""" + global drone, drone_armed init_pymavlink() drone = System() await drone.connect(system_address=MAVLINK_ADDRESS) @@ -212,6 +215,8 @@ async def main(): asyncio.ensure_future(monitor_armed(drone)) break +async def main(): + await init() print("\nWireClaw pret") print("Commandes : takeoff, goto, orbit, status, position, hover, rtl, land\n")