Private
Public Access
added point mode
This commit is contained in:
1 parent
044ad2afbe
commit
fce8f62534
16 files changed
+1547
-10
No files matched your search
+53
-1
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user