added point mode

This commit is contained in:
marcinator_2 committed 2026-10-15 20:31:02 +02:00
1 parent 044ad2afbe
commit fce8f62534
16 files changed
+1547 -10

No files matched your search

+53 -1
View File
@@ -1,4 +1,4 @@
"""Lokale WebUI und MJPEG-Livebild; alle Aktoren bleiben simuliert."""
"""Lokale WebUI und MJPEG-Livebild; Grabaktoren simuliert, Kameraservos separat freigebbar."""
from contextlib import asynccontextmanager, suppress
from pathlib import Path
@@ -15,6 +15,7 @@ from .camera import CameraStream
from .controller import Controller, MODES
from .event_store import EventStore
from .detection import DetectionService
from .follow import FollowService
PAGE = (Path(__file__).parent / "index.html").read_text(encoding="utf-8")
@@ -46,6 +47,9 @@ def create_app(demo: bool = False, state_dir: Path | None = None) -> FastAPI:
events = EventStore(directory)
detection = DetectionService(camera, controller, events, directory)
follow = FollowService(camera, directory)
follow_api_lock = asyncio.Lock()
@asynccontextmanager
async def lifespan(app: FastAPI):
async def periodic_profile_cleanup():
@@ -59,16 +63,19 @@ def create_app(demo: bool = False, state_dir: Path | None = None) -> FastAPI:
async def detection_watchdog():
while True:
detection.watchdog()
follow.watchdog()
await asyncio.sleep(0.1)
camera.start()
detection.start()
follow.start()
watchdog_task = asyncio.create_task(detection_watchdog())
cleanup_task = asyncio.create_task(periodic_profile_cleanup())
try:
yield
finally:
controller.stop()
follow.stop()
watchdog_task.cancel()
with suppress(asyncio.CancelledError):
await watchdog_task
@@ -83,6 +90,7 @@ def create_app(demo: bool = False, state_dir: Path | None = None) -> FastAPI:
app.state.controller = controller
app.state.events = events
app.state.detection = detection
app.state.follow = follow
@app.middleware("http")
async def security_headers(request: Request, call_next):
@@ -112,6 +120,8 @@ def create_app(demo: bool = False, state_dir: Path | None = None) -> FastAPI:
@app.post("/api/detection/config", dependencies=[Depends(require_ui)])
async def configure_detection(payload: dict):
if follow.active and payload.get("enabled"):
raise HTTPException(status_code=409, detail="Zuerst Kameranachführung stoppen")
try:
return detection.configure(payload)
except (ValueError, TypeError, KeyError, IndexError) as exc:
@@ -119,6 +129,46 @@ def create_app(demo: bool = False, state_dir: Path | None = None) -> FastAPI:
except OSError as exc:
raise HTTPException(status_code=500, detail=f"Einstellung nicht gespeichert: {exc}") from exc
@app.get("/api/follow")
async def follow_status():
return follow.status()
@app.post("/api/follow/{action}", dependencies=[Depends(require_ui)])
async def follow_action(action: str, payload: dict):
async with follow_api_lock:
try:
if action == "config":
return follow.configure(payload)
if action == "capture":
return follow.capture()
if action == "select":
return follow.select(payload.get("token"), payload.get("x"), payload.get("y"))
if action == "start":
if detection.get_config()["enabled"]:
raise ValueError("Zuerst automatische Auslösung ausschalten und speichern")
if type(payload.get("hardware", False)) is not bool:
raise ValueError("Ungültige Servofreigabe")
controller.stop()
session = follow.begin(payload.get("hardware", False),
payload.get("acknowledged") is True,
payload.get("operation", "follow"))
return {"session": session}
if action == "jog":
return follow.jog(payload.get("session"), payload.get("direction"), payload.get("sequence"))
if action == "remember":
return follow.remember(payload.get("session"), payload.get("direction"))
if action == "heartbeat":
follow.heartbeat(payload.get("session"))
return {"ok": True}
if action == "stop":
follow.halt()
return follow.status()
raise HTTPException(status_code=404, detail="Unbekannte Aktion")
except (ValueError, TypeError) as exc:
raise HTTPException(status_code=422, detail=str(exc)) from exc
except OSError as exc:
raise HTTPException(status_code=503, detail=str(exc)) from exc
@app.get("/api/events")
async def recent_events(limit: int = Query(20, ge=1, le=100)):
try:
@@ -213,11 +263,13 @@ def create_app(demo: bool = False, state_dir: Path | None = None) -> FastAPI:
@app.post("/api/stop", dependencies=[Depends(require_ui)])
async def stop():
follow.halt()
controller.stop()
return controller.status()
@app.post("/api/reset", dependencies=[Depends(require_ui)])
async def reset():
follow.halt()
controller.reset()
detection.reset()
return controller.status()