feat(perception): project recorded results into Rerun
This commit is contained in:
@@ -13,6 +13,7 @@ from pydantic import BaseModel, ConfigDict, Field, StrictBool, field_validator,
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
from starlette.types import Receive, Scope, Send
|
||||
|
||||
from k1link.compute import RecordedPerceptionOverlayError
|
||||
from k1link.sessions import (
|
||||
RECORDED_MEDIA_MANIFEST_SCHEMA,
|
||||
LayoutConflictError,
|
||||
@@ -102,6 +103,16 @@ class RecordedBlueprintRequest(StrictApiModel):
|
||||
point_size: float = Field(default=2.5, strict=True, ge=0.1, le=32.0)
|
||||
palette: Literal["turbo", "viridis", "plasma", "grayscale", "custom"] = "turbo"
|
||||
custom_color: str = Field(default="#f7f8f4", pattern=r"^#[0-9A-Fa-f]{6}$")
|
||||
active_view: Literal["spatial", "perception", "metrics"] = "spatial"
|
||||
|
||||
|
||||
class RecordedPerceptionRequest(StrictApiModel):
|
||||
application_id: Literal["nodedc_mission_core_recorded"]
|
||||
recording_id: str = Field(
|
||||
min_length=1,
|
||||
max_length=128,
|
||||
pattern=r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$",
|
||||
)
|
||||
|
||||
|
||||
class SceneSettingsDocument(StrictApiModel):
|
||||
@@ -233,6 +244,16 @@ class CatalogRefresher(Protocol):
|
||||
def __call__(self) -> object: ...
|
||||
|
||||
|
||||
class RecordedPerceptionOverlayProvider(Protocol):
|
||||
def render(
|
||||
self,
|
||||
session_id: str,
|
||||
*,
|
||||
application_id: str,
|
||||
recording_id: str,
|
||||
) -> bytes | None: ...
|
||||
|
||||
|
||||
def build_session_router(
|
||||
store: SessionStore,
|
||||
*,
|
||||
@@ -241,6 +262,7 @@ def build_session_router(
|
||||
recording_materializer: RecordingMaterializer | None = None,
|
||||
recording_preparation_manager: SessionRecordingPreparationManager | None = None,
|
||||
media_inspector: RecordedMediaInspector | None = None,
|
||||
perception_overlay_provider: RecordedPerceptionOverlayProvider | None = None,
|
||||
allow_synchronous_recording_fallback: bool = False,
|
||||
replay_action_id: str = DEFAULT_REPLAY_ACTION_ID,
|
||||
) -> APIRouter:
|
||||
@@ -755,6 +777,7 @@ def build_session_router(
|
||||
),
|
||||
application_id=RECORDED_APPLICATION_ID,
|
||||
recording_id=request.recording_id,
|
||||
active_view=request.active_view,
|
||||
)
|
||||
except SessionNotFoundError as exc:
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
@@ -780,6 +803,56 @@ def build_session_router(
|
||||
},
|
||||
)
|
||||
|
||||
@router.post("/api/v1/observation-sessions/{session_id}/perception.rrd")
|
||||
async def get_observation_session_perception(
|
||||
session_id: str,
|
||||
request: RecordedPerceptionRequest,
|
||||
) -> Response:
|
||||
if perception_overlay_provider is None:
|
||||
return Response(status_code=204, headers={"Cache-Control": "no-store"})
|
||||
try:
|
||||
await run_in_threadpool(
|
||||
_prepare_replay,
|
||||
store,
|
||||
catalog_refresher,
|
||||
session_id,
|
||||
1.0,
|
||||
False,
|
||||
False,
|
||||
)
|
||||
payload = await run_in_threadpool(
|
||||
perception_overlay_provider.render,
|
||||
session_id,
|
||||
application_id=request.application_id,
|
||||
recording_id=request.recording_id,
|
||||
)
|
||||
except SessionNotFoundError as exc:
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
except (SessionNotReplayableError, SessionIntegrityError) as exc:
|
||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||
except RecordedPerceptionOverlayError as exc:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Не удалось подготовить слой распознавания.",
|
||||
) from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="Некорректный идентификатор сессии.",
|
||||
) from exc
|
||||
if payload is None:
|
||||
return Response(status_code=204, headers={"Cache-Control": "no-store"})
|
||||
return Response(
|
||||
content=payload,
|
||||
media_type="application/vnd.rerun.rrd",
|
||||
headers={
|
||||
"Cache-Control": "private, no-cache, no-transform",
|
||||
"Content-Length": str(len(payload)),
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"Content-Disposition": 'inline; filename="perception.rrd"',
|
||||
},
|
||||
)
|
||||
|
||||
@router.get("/api/v1/observation-sessions/{session_id}/media/{artifact_id}/manifest")
|
||||
def get_recorded_media_manifest(
|
||||
session_id: str,
|
||||
|
||||
Reference in New Issue
Block a user