From a281faf923f581feb1fba84d7d68f4d79fdbd415 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Thu, 16 Jul 2026 20:56:41 +0300 Subject: [PATCH] feat(observation): add dynamic camera sources --- README.md | 4 +- apps/control-station/src/App.tsx | 9 +- .../components/FloatingObservationWindow.tsx | 78 +++ .../src/components/ObservationSources.tsx | 149 ++++++ .../src/components/ObservationTimeline.tsx | 52 ++ .../core/observation/useObservationLayout.ts | 137 ++++++ .../src/core/runtime/contracts.ts | 66 +++ .../src/device-plugins/xgrids-k1/api.ts | 17 + .../xgrids-k1/observationSources.ts | 153 ++++++ .../xgrids-k1/runtimeContext.tsx | 11 + apps/control-station/src/styles.css | 1 + .../src/styles/observation.css | 451 ++++++++++++++++++ .../src/workspaces/Workspaces.tsx | 241 ++++++++-- .../test/observationSources.test.mjs | 197 ++++++++ ...ervation-sources-and-live-only-timeline.md | 78 +++ 15 files changed, 1592 insertions(+), 52 deletions(-) create mode 100644 apps/control-station/src/components/FloatingObservationWindow.tsx create mode 100644 apps/control-station/src/components/ObservationSources.tsx create mode 100644 apps/control-station/src/components/ObservationTimeline.tsx create mode 100644 apps/control-station/src/core/observation/useObservationLayout.ts create mode 100644 apps/control-station/src/device-plugins/xgrids-k1/observationSources.ts create mode 100644 apps/control-station/src/styles/observation.css create mode 100644 apps/control-station/test/observationSources.test.mjs create mode 100644 docs/adr/0006-vendor-neutral-observation-sources-and-live-only-timeline.md diff --git a/README.md b/README.md index f35e57d..a8888a3 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,9 @@ immutable donor revision remains a packaging and CI prerequisite. The Observation spatial workspace embeds the open-source Rerun Web Viewer inside the Mission Core shell. It can open a compatible RRD file over HTTP(S) or a Rerun gRPC/proxy source such as `rerun+http://127.0.0.1:9876/proxy`. It does not -use an external hosted viewer UI. +use an external hosted viewer UI. Dynamic point-cloud and camera source +composition, host-owned window layout and the current live-only timeline contract +are fixed in [`ADR 0006`](docs/adr/0006-vendor-neutral-observation-sources-and-live-only-timeline.md). The first K1 live session or replay in a `k1link serve` process creates one local Rerun `RecordingStream`, starts its gRPC/proxy server on TCP 9876 and publishes diff --git a/apps/control-station/src/App.tsx b/apps/control-station/src/App.tsx index ee55016..5d75cd7 100644 --- a/apps/control-station/src/App.tsx +++ b/apps/control-station/src/App.tsx @@ -29,6 +29,7 @@ import { LandingStage } from "./components/LandingStage"; import { useDevicePluginHost } from "./core/device-plugins/DevicePluginHost"; import { useMissionRuntime } from "./core/runtime/MissionRuntimeContext"; import type { ViewerSettings } from "./core/runtime/contracts"; +import { useObservationLayout } from "./core/observation/useObservationLayout"; import { rootById, roots, @@ -132,6 +133,7 @@ export default function App() { const activeSceneWindow = sceneWindowOrder[sceneWindowOrder.length - 1] ?? null; const automaticSourceUrl = runtime.state?.spatialSource?.url.trim() ?? ""; const effectiveSourceUrl = sourceUrl || automaticSourceUrl; + const observationLayout = useObservationLayout(runtime.state?.observationSources ?? []); useEffect(() => { const remote = runtime.state?.viewerSettings; @@ -238,7 +240,7 @@ export default function App() { if (activeDefinition?.kind === "spatial") { actions.push( - { label: "Настроить источник", icon: "network", onClick: openSource }, + { label: "Настроить визуальный движок", icon: "network", onClick: openSource }, { label: "Настроить отображение", icon: "sliders", onClick: openDisplay }, { label: "Открыть слои", icon: "list", onClick: openLayers }, ); @@ -370,6 +372,7 @@ export default function App() { backendStatus={runtime.backendStatus} sourceUrl={effectiveSourceUrl} sceneSettings={sceneSettings} + observationLayout={observationLayout} navigation={{ openView, openSource, @@ -384,8 +387,8 @@ export default function App() { ; + rect?: ObservationWindowRect; + maximized: boolean; + active: boolean; + onRectChange: (rect: ObservationWindowRect) => void; + onMaximizedChange: (maximized: boolean) => void; + onActivate: () => void; + onClose: () => void; +}) { + return ( + +