refactor(lab): restore canonical RAV004 replay

This commit is contained in:
DCCONSTRUCTIONS
2026-08-30 01:22:44 +03:00
parent 74da6437e9
commit f1cbe0061a
21 changed files with 1181 additions and 719 deletions
@@ -153,6 +153,7 @@ export interface M4ReplayClassifiedSpatialLayer {
label: string;
pointLayerLabel: string;
cellLayerLabel: string;
cellLayerAvailable?: boolean;
expectedAtSequence: boolean;
frame: M4ReplayClassifiedSpatialFrame | null;
loading: boolean;
@@ -178,6 +179,8 @@ export function M4ReplayThreatVisual({
classifiedSpatialLayer,
showReferenceMediaLayers = true,
showSpatialOverlaySummary = true,
playbackTransport = "epoch-stream",
recoverTimestampStalls = false,
onActiveSequenceChange,
}: {
resultId: string;
@@ -194,6 +197,8 @@ export function M4ReplayThreatVisual({
classifiedSpatialLayer?: M4ReplayClassifiedSpatialLayer;
showReferenceMediaLayers?: boolean;
showSpatialOverlaySummary?: boolean;
playbackTransport?: "segmented" | "epoch-stream";
recoverTimestampStalls?: boolean;
onActiveSequenceChange?: (sequence: number | null) => void;
}) {
const {
@@ -256,7 +261,7 @@ export function M4ReplayThreatVisual({
showMediaSemantic: Boolean(activeSemantic) && showMediaSemantic,
showSpatialSemantic: Boolean(activeSpatialSemantic) && showSpatialSemantic,
showMediaPoints,
classifiedSpatialMode: !classifiedSpatialLayer
classifiedSpatialMode: !classifiedSpatialLayer || classifiedSpatialLayer.cellLayerAvailable === false
? "none"
: classifiedSpatialLayer.replacePointCloud
? "replace-source"
@@ -278,7 +283,7 @@ export function M4ReplayThreatVisual({
endSeconds: metadata.timeline.timelineEndSeconds,
}) : null, [metadata.timeline]);
const playbackController = useRecordedEvidencePlayback(playbackRange, {
clock: "animation",
clock: "external",
});
const seekPlayback = playbackController.seek;
const setPlaybackPlaying = playbackController.setPlaying;
@@ -356,11 +361,19 @@ export function M4ReplayThreatVisual({
useEffect(() => {
lastSpatialFrameRef.current = null;
}, [evidenceDemand.sourceSpatialPoints, resultId]);
if (frame?.spatialAvailable) {
lastSpatialFrameRef.current = { resultId, frame };
const latestAvailableSpatialFrame = [...timelineFrame.availableFrames]
.reverse()
.find((candidate) => (
candidate.spatialAvailable
&& (timelineFrame.activeSequence === null
|| candidate.sequence <= timelineFrame.activeSequence)
)) ?? null;
const currentSpatialFrame = frame?.spatialAvailable ? frame : latestAvailableSpatialFrame;
if (currentSpatialFrame) {
lastSpatialFrameRef.current = { resultId, frame: currentSpatialFrame };
}
const spatialFrame = frame?.spatialAvailable
? frame
const spatialFrame = currentSpatialFrame
? currentSpatialFrame
: lastSpatialFrameRef.current?.resultId === resultId
? lastSpatialFrameRef.current.frame
: null;
@@ -541,14 +554,20 @@ export function M4ReplayThreatVisual({
const activeSpatialFrame = spatialFrame?.sequence === timelineFrame.activeSequence
? spatialFrame
: null;
const classifiedSpatialFrame = classifiedSpatialLayer?.frame?.sourceSequence === timelineFrame.activeSequence
const hasClassifiedSpatialOutput = Boolean(
classifiedSpatialLayer && classifiedSpatialLayer.cellLayerAvailable !== false,
);
const classifiedSpatialFrame = hasClassifiedSpatialOutput
&& classifiedSpatialLayer?.frame?.sourceSequence === timelineFrame.activeSequence
? classifiedSpatialLayer?.frame ?? null
: null;
const lastClassifiedSpatialFrameRef = useRef<{
resultId: string;
frame: M4ReplayClassifiedSpatialFrame;
} | null>(null);
const incomingClassifiedSpatialFrame = classifiedSpatialLayer?.frame ?? null;
const incomingClassifiedSpatialFrame = hasClassifiedSpatialOutput
? classifiedSpatialLayer?.frame ?? null
: null;
if (incomingClassifiedSpatialFrame && incomingClassifiedSpatialFrame.sampleAvailable !== false) {
lastClassifiedSpatialFrameRef.current = { resultId, frame: incomingClassifiedSpatialFrame };
}
@@ -577,7 +596,9 @@ export function M4ReplayThreatVisual({
? spatialFrame
: null)
: null;
const replaceClassifiedPointCloud = classifiedSpatialLayer?.replacePointCloud ?? true;
const replaceClassifiedPointCloud = hasClassifiedSpatialOutput
? classifiedSpatialLayer?.replacePointCloud ?? true
: false;
const nominalSensorHeightM = metadata.timeline?.rig.nominalSensorHeightM ?? 0;
const mapGravityLocalSensorToBodyGround = useCallback((
point: readonly [number, number, number],
@@ -695,7 +716,12 @@ export function M4ReplayThreatVisual({
.map((item) => item.assessment.closestApproachM)
.filter((value): value is number => value !== null)
.sort((left, right) => left - right)[0] ?? null;
const localSurface = useMemo(() => buildM4LocalSurface(
const localSurface = useMemo(() => spatialFrame?.localSlamBodyXyzM?.length ? ({
pointsBodyXyzM: spatialFrame.localSlamBodyXyzM,
sourceFrameCount: spatialFrame.localSlamSourceFrameCount ?? 0,
sourcePointCount: spatialFrame.localSlamSourcePointCount ?? 0,
voxelCount: spatialFrame.localSlamBodyXyzM.length,
}) : buildM4LocalSurface(
timelineFrame.availableFrames,
spatialFrame,
metadata.timeline?.localSurfaceVisualization ?? {
@@ -843,16 +869,24 @@ export function M4ReplayThreatVisual({
shape="pill"
variant={showRollingMap ? "primary" : "secondary"}
aria-pressed={showRollingMap}
disabled={classifiedSpatialLayer.cellLayerAvailable === false}
title={classifiedSpatialLayer.cellLayerAvailable === false
? `${classifiedSpatialLayer.cellLayerLabel} недоступен: для этой записи нет запечатанного полного результата`
: undefined}
onClick={() => setShowRollingMap((visible) => !visible)}
>
{classifiedSpatialLayer.cellLayerLabel}
</Button>
{semanticSpatialResultId ? (
{activeSpatialSemantic ? (
<Button
size="compact"
shape="pill"
variant={showSpatialSemantic ? "primary" : "secondary"}
aria-pressed={showSpatialSemantic}
disabled={!semanticSpatialResultId}
title={semanticSpatialResultId
? "Point-aligned semantic evidence"
: "Point-aligned 3D semantics отсутствует в запечатанном результате"}
onClick={() => setShowSpatialSemantic((visible) => !visible)}
>
SEMANTICS
@@ -905,12 +939,16 @@ export function M4ReplayThreatVisual({
LOW-STEP
</Button>
) : null}
{semanticSpatialResultId ? (
{activeSpatialSemantic ? (
<Button
size="compact"
shape="pill"
variant={showSpatialSemantic ? "primary" : "secondary"}
aria-pressed={showSpatialSemantic}
disabled={!semanticSpatialResultId}
title={semanticSpatialResultId
? "Point-aligned semantic evidence"
: "Point-aligned 3D semantics отсутствует в запечатанном результате"}
onClick={() => setShowSpatialSemantic((visible) => !visible)}
>
SEMANTICS
@@ -1014,14 +1052,14 @@ export function M4ReplayThreatVisual({
<>
<div>
<span>Spatial evidence</span>
<strong>{classifiedSpatialLayer
<strong>{hasClassifiedSpatialOutput
? classifiedSpatialFrame
? replaceClassifiedPointCloud
? `${classifiedSpatialFrame.pointsMapGravityLocalXyzM.length.toLocaleString("ru-RU")} TGS points · ${classifiedCellCount.toLocaleString("ru-RU")} cells`
: `${(activeSpatialFrame?.pointCloudSourceCount ?? classifiedSpatialFrame.sourcePointCount ?? 0).toLocaleString("ru-RU")} source points · ${classifiedCellCount.toLocaleString("ru-RU")} TGS cells`
: "TGS spatial buffer"
: `${currentIncrementObstacles.length} current · ${rollingMapObstacles.length} rolling${metadata.timeline.occupancyProvenanceDelivery ? ` · ${lowStepObstacles.length} low-step` : ""}`}</strong>
<small>{classifiedSpatialLayer
<small>{hasClassifiedSpatialOutput
? classifiedSpatialFrame
? classifiedSpatialFrame.sampleAvailable === false
? displayedClassifiedFrameHeld && displayedClassifiedSpatialFrame
@@ -1030,9 +1068,9 @@ export function M4ReplayThreatVisual({
: activeSpatialFrame
? "map-gravity-local · all eligible points accounted · causal rolling 1 s"
: "TGS рассчитан · linked source cloud недоступен для этого кадра"
: classifiedSpatialLayer.error
?? classifiedSpatialLayer.loadingLabel
?? `Открываем ${classifiedSpatialLayer.label}`
: classifiedSpatialLayer?.error
?? classifiedSpatialLayer?.loadingLabel
?? `Открываем ${classifiedSpatialLayer?.label ?? "spatial evidence"}`
: (
<>
{spatialFrame
@@ -1057,13 +1095,13 @@ export function M4ReplayThreatVisual({
)}</small>
</div>
<div>
<span>{classifiedSpatialLayer ? "TGS fail-closed" : "Virtual corridor"}</span>
<strong>{classifiedSpatialLayer
<span>{hasClassifiedSpatialOutput ? "TGS fail-closed" : "Virtual corridor"}</span>
<strong>{hasClassifiedSpatialOutput
? classifiedSpatialFrame
? `${classifiedCellCounts.occupied} occupied · ${classifiedCellCounts.rejected} rejected · ${classifiedCellCounts.unobserved} unobserved`
: classifiedSpatialLayer.loading || displayingBufferedFrame ? "loading" : "unavailable"
: classifiedSpatialLayer?.loading || displayingBufferedFrame ? "loading" : "unavailable"
: `${spatialFrame?.decisionCounts.threat ?? 0} threat · nearest ${nearest === null ? "—" : `${nearest.toFixed(2)} м`}`}</strong>
<small>{classifiedSpatialLayer
<small>{hasClassifiedSpatialOutput
? classifiedSpatialFrame
? `${classifiedCellCounts.ground} ground-support · visual review only · navigation authority OFF`
: "visual review only · navigation authority OFF"
@@ -1116,8 +1154,9 @@ export function M4ReplayThreatVisual({
}
segmentCount={timeline.frameCount}
onPlaybackChange={playbackController.synchronize}
playbackAuthority="host"
playbackTransport="epoch-stream"
playbackAuthority="media"
playbackTransport={playbackTransport}
recoverTimestampStalls={recoverTimestampStalls}
/>
) : videoError ? (
<SpatialState message={videoError} />
@@ -1148,9 +1187,11 @@ export function M4ReplayThreatVisual({
ref={metricSceneRef}
pointCloudBodyXyzM={displayedClassifiedSpatialFrame && replaceClassifiedPointCloud
? classifiedPointsBody
: classifiedContextSpatialFrame?.pointCloudBodyXyzM ?? []}
: classifiedContextSpatialFrame?.pointCloudBodyXyzM
?? activeSpatialFrame?.pointCloudBodyXyzM
?? []}
localSurfaceBodyXyzM={localSurface.pointsBodyXyzM}
obstacles={classifiedSpatialLayer ? [] : sceneObstacles}
obstacles={hasClassifiedSpatialOutput ? [] : sceneObstacles}
rig={timeline.rig}
corridor={timeline.corridor}
occupiedVoxelSizeM={displayedClassifiedSpatialFrame?.cellSizeM ?? timeline.occupiedVoxelSizeM}
@@ -1159,7 +1200,7 @@ export function M4ReplayThreatVisual({
showCurrentIncrement={showCurrentIncrement}
showLocalSurface={showLocalSurface}
showRollingMap={showRollingMap}
showLowStep={classifiedSpatialLayer ? false : showLowStep}
showLowStep={hasClassifiedSpatialOutput ? false : showLowStep}
pointSemanticClassIds={displayedClassifiedSpatialFrame && replaceClassifiedPointCloud
? displayedClassifiedSpatialFrame.pointClassIds
: alignedSemanticPointIds}
@@ -1174,7 +1215,7 @@ export function M4ReplayThreatVisual({
classifiedCellSizeM={displayedClassifiedSpatialFrame?.cellSizeM}
showClassifiedCells={showRollingMap}
/>
{classifiedSpatialLayer && !displayedClassifiedSpatialFrame ? (
{hasClassifiedSpatialOutput && classifiedSpatialLayer && !displayedClassifiedSpatialFrame ? (
<div className="l3-visual-audit__state" role={classifiedSpatialLayer.error ? "alert" : "status"}>
{classifiedSpatialLayer.loading || displayingBufferedFrame
? <span className="busy-indicator" aria-hidden="true" />
@@ -1,30 +1,5 @@
import {
useEffect,
useMemo,
useRef,
useState,
type CSSProperties,
} from "react";
import {
Button,
Icon,
IconButton,
SegmentedControl,
} from "@nodedc/ui-react";
import { useEffect, useMemo, useState } from "react";
import { ObservationTimeline } from "../../components/ObservationTimeline";
import {
CanonicalRecordedLabReplay,
useCanonicalRecordedLabReplayState,
} from "../../components/laboratory/CanonicalRecordedLabReplay";
import {
LaboratoryMetricEvidenceScene,
type LaboratoryMetricEvidenceSceneHandle,
type LaboratoryMetricPackedCellEvidence,
} from "../../components/laboratory/LaboratoryMetricEvidenceScene";
import { RecordedEvidenceVideoScene } from "../../components/laboratory/RecordedEvidenceVideoScene";
import { useCanonicalRecordedLabSpatialFrame } from "../../components/laboratory/useCanonicalRecordedLabSpatialFrame";
import { useRecordedEvidencePlayback } from "../../components/laboratory/useRecordedEvidencePlayback";
import {
LaboratoryEvidence,
LaboratoryResultSummary,
@@ -32,23 +7,9 @@ import {
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import {
type RecordedEvidenceSemanticClass,
type RecordedEvidenceSemanticPaletteEntry,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay";
import {
canonicalRecordedLabPackedTgsCells,
canonicalRecordedLabTgsIsCurrent,
} from "../../core/laboratory/canonicalRecordedLab";
import {
fetchVegetationShadowResult,
fetchVegetationRouteTgsAnchor,
vegetationFullRouteMaskUrl,
vegetationVideoMaskUrl,
type VegetationFullRouteLayer,
type VegetationFullRouteReview,
type VegetationMixedRouteCase,
type VegetationMixedRouteReview,
type VegetationRouteTgsAnchor,
type VegetationShadowResult,
} from "../../core/laboratory/vegetationShadow";
import {
@@ -56,81 +17,18 @@ import {
type M49TgsFullShadowResult,
} from "../../core/laboratory/m49TgsFullShadow";
import { M49TgsFullShadowEvidence } from "./M49TgsFullShadowEvidence";
import { recordedObservationSources } from "../../core/observation/recordedObservationSources";
import type { ObservationSessionReplayLaunch } from "../../core/observation/sessionArchive";
import { resolveObservationSessionReplay } from "../../core/observation/useObservationSessions";
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
import {
M4ReplayThreatVisual,
type M4ReplayClassifiedSpatialLayer,
type M4ReplayThreatSemanticLayer,
} from "./M4ReplayThreatVisual";
const VEGETATION_TIMELINE_ENDPOINT = "/api/v1/laboratory/vegetation-shadow";
function decimal(value: number, digits = 1): string {
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
}
const FULL_ROUTE_SEMANTIC_MODES = [
{ value: "city", label: "ГОРОД · EoMT" },
{ value: "vegetation", label: "ПРИРОДА · DDRNet" },
] as const;
type FullRouteMediaMode = "video" | "camera";
type FullRouteSpatialMode = "3d" | "plan";
const FULL_ROUTE_MEDIA_MODES = [
{ value: "video", label: "VIDEO" },
{ value: "camera", label: "CAMERA" },
] as const;
const FULL_ROUTE_SPATIAL_MODES = [
{ value: "3d", label: "3D" },
{ value: "plan", label: "PLAN" },
] as const;
function semanticPresentation(layer: VegetationFullRouteLayer): {
classes: readonly RecordedEvidenceSemanticClass[];
palette: readonly RecordedEvidenceSemanticPaletteEntry[];
} {
return {
classes: layer.taxonomy.map((item) => ({ id: item.classId, label: item.label })),
palette: layer.taxonomy.map((item) => ({
classId: item.classId,
color: item.classId === 0
? { kind: "transparent" as const }
: { kind: "diagnostic" as const, rgb: item.colorRgb },
})),
};
}
function causalTgsCase(
cases: readonly VegetationMixedRouteCase[],
sequence: number,
): VegetationMixedRouteCase | null {
if (!cases.length) return null;
return cases.reduce<VegetationMixedRouteCase | null>((latest, candidate) => (
candidate.sourceSequence <= sequence
&& (!latest || candidate.sourceSequence > latest.sourceSequence)
? candidate
: latest
), null);
}
function nearestFullRouteFrameIndex(
frameSourceTimesNs: readonly number[],
sourceTimeNs: number,
): number {
if (!frameSourceTimesNs.length) return 0;
let low = 0;
let high = frameSourceTimesNs.length - 1;
while (low < high) {
const middle = Math.floor((low + high) / 2);
if ((frameSourceTimesNs[middle] ?? 0) < sourceTimeNs) low = middle + 1;
else high = middle;
}
if (low === 0) return 0;
const previous = frameSourceTimesNs[low - 1] ?? frameSourceTimesNs[0] ?? 0;
const current = frameSourceTimesNs[low] ?? previous;
return Math.abs(sourceTimeNs - previous) <= Math.abs(current - sourceTimeNs)
? low - 1
: low;
}
function FullRouteReviewEvidence({
resultId,
review,
@@ -138,425 +36,53 @@ function FullRouteReviewEvidence({
resultId: string;
review: VegetationFullRouteReview;
}) {
const {
mediaMode,
spatialMode,
splitView,
splitPrimarySize,
splitOrientation,
expanded,
onMediaModeChange: handleMediaModeChange,
onSpatialModeChange: handleSpatialModeChange,
onSplitPrimarySizeChange: setSplitPrimarySize,
onExpandedChange: setExpanded,
} = useCanonicalRecordedLabReplayState<FullRouteMediaMode, FullRouteSpatialMode>({
initialMediaMode: "video",
initialSpatialMode: "3d",
});
const [semanticLayer, setSemanticLayer] = useState<"city" | "vegetation">("vegetation");
const [showCameraSemantic, setShowCameraSemantic] = useState(true);
const [showSourcePoints, setShowSourcePoints] = useState(true);
const [showLocalSlam, setShowLocalSlam] = useState(true);
const [showTgs, setShowTgs] = useState(true);
const [videoSource, setVideoSource] = useState<ObservationSourceDescriptor | null>(null);
const [replayLaunch, setReplayLaunch] = useState<ObservationSessionReplayLaunch | null>(null);
const [videoError, setVideoError] = useState<string | null>(null);
const [linkedReview, setLinkedReview] = useState<VegetationMixedRouteReview | null>(null);
const [linkedReviewError, setLinkedReviewError] = useState<string | null>(null);
const [tgsAnchor, setTgsAnchor] = useState<VegetationRouteTgsAnchor | null>(null);
const [tgsAnchorLoading, setTgsAnchorLoading] = useState(false);
const [tgsAnchorError, setTgsAnchorError] = useState<string | null>(null);
const metricSceneRef = useRef<LaboratoryMetricEvidenceSceneHandle | null>(null);
const playbackRange = useMemo(() => ({
startSeconds: review.timelineStartSeconds,
endSeconds: review.timelineEndSeconds,
}), [review.timelineEndSeconds, review.timelineStartSeconds]);
const playbackController = useRecordedEvidencePlayback(playbackRange, { clock: "animation" });
const sequenceIndex = nearestFullRouteFrameIndex(
review.frameSourceTimesNs,
Math.round(playbackController.playback.currentSeconds * 1_000_000_000),
);
const sequence = sequenceIndex + 1;
const spatialRequestIndex = Math.floor(sequenceIndex / 5) * 5;
const spatialRequestTimeNs = review.frameSourceTimesNs[spatialRequestIndex]
?? review.frameSourceTimesNs[sequenceIndex]
?? Math.round(playbackController.playback.currentSeconds * 1_000_000_000);
const spatialEvidence = useCanonicalRecordedLabSpatialFrame({
sessionId: review.sessionId,
generationSha256: replayLaunch?.sha256 ?? null,
targetTimeNs: spatialRequestTimeNs,
});
const layer = review[semanticLayer];
const semantic = useMemo(() => semanticPresentation(layer), [layer]);
const prefetchSrcs = useMemo(() => showCameraSemantic
? Array.from({ length: 8 }, (_, offset) => sequenceIndex + offset + 1)
.filter((candidate) => candidate < review.frameCount)
.map((candidate) => vegetationFullRouteMaskUrl(resultId, semanticLayer, candidate))
: [], [resultId, review.frameCount, semanticLayer, sequenceIndex, showCameraSemantic]);
useEffect(() => {
const controller = new AbortController();
setVideoSource(null);
setReplayLaunch(null);
setVideoError(null);
void resolveObservationSessionReplay(review.sessionId, { signal: controller.signal })
.then((launch) => {
const source = recordedObservationSources(launch).find((candidate) => (
candidate.id === review.recordedMediaSourceId
&& candidate.modality === "video"
&& candidate.semanticChannelId === "camera.video.recorded"
&& candidate.delivery?.kind === "recorded-fmp4-manifest"
&& candidate.delivery.manifestGenerationSha256 === review.recordedMediaGenerationSha256
&& candidate.delivery.timelineStartSeconds === review.timelineStartSeconds
&& candidate.delivery.timelineEndSeconds >= review.timelineEndSeconds
));
if (!source) {
throw new Error("RIGHT-видео не совпало с sealed RAVNOVES004TREE timeline.");
}
if (!controller.signal.aborted) {
setVideoSource(source);
setReplayLaunch(launch);
}
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) {
setVideoError(caught instanceof Error ? caught.message : "Записанное видео недоступно.");
}
});
return () => controller.abort();
}, [
review.recordedMediaGenerationSha256,
review.recordedMediaSourceId,
review.sessionId,
review.timelineEndSeconds,
review.timelineStartSeconds,
]);
useEffect(() => {
const controller = new AbortController();
setLinkedReview(null);
setLinkedReviewError(null);
void fetchVegetationShadowResult(review.linkedRouteReviewResultId, {
signal: controller.signal,
}).then((result) => {
if (
!result.routeReview
|| result.routeReview.sourceId !== review.sourceId
|| result.routeReview.sessionId !== review.sessionId
) {
throw new Error("TGS anchors имеют другую source identity.");
}
if (!controller.signal.aborted) setLinkedReview(result.routeReview);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setLinkedReviewError(caught instanceof Error ? caught.message : "TGS anchors недоступны.");
}
});
return () => controller.abort();
}, [review.linkedRouteReviewResultId, review.sessionId, review.sourceId]);
const selectedTgsCase = linkedReview
? causalTgsCase(linkedReview.cases, sequence)
: null;
const selectedTgsTimeNs = selectedTgsCase
? review.frameSourceTimesNs[selectedTgsCase.sourceSequence - 1]
?? Math.round(selectedTgsCase.sessionSeconds * 1_000_000_000)
: spatialRequestTimeNs;
const currentFrameTimeNs = review.frameSourceTimesNs[sequenceIndex]
?? Math.round(playbackController.playback.currentSeconds * 1_000_000_000);
const tgsWithinEvidenceWindow = Boolean(
selectedTgsCase
&& canonicalRecordedLabTgsIsCurrent(currentFrameTimeNs, selectedTgsTimeNs),
);
const tgsReferenceEvidence = useCanonicalRecordedLabSpatialFrame({
sessionId: review.sessionId,
generationSha256: replayLaunch?.sha256 ?? null,
targetTimeNs: selectedTgsTimeNs,
});
useEffect(() => {
if (!showTgs || !selectedTgsCase) {
setTgsAnchor(null);
setTgsAnchorLoading(false);
setTgsAnchorError(null);
return;
}
const controller = new AbortController();
setTgsAnchorLoading(true);
setTgsAnchorError(null);
void fetchVegetationRouteTgsAnchor(
review.linkedRouteReviewResultId,
selectedTgsCase.sourceSequence,
{ signal: controller.signal },
).then((anchor) => {
if (!controller.signal.aborted) setTgsAnchor(anchor);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) {
setTgsAnchorError(caught instanceof Error ? caught.message : "TGS anchor недоступен.");
}
}).finally(() => {
if (!controller.signal.aborted) setTgsAnchorLoading(false);
});
return () => controller.abort();
}, [review.linkedRouteReviewResultId, selectedTgsCase?.sourceSequence, showTgs]);
const packedTgsCells = useMemo<LaboratoryMetricPackedCellEvidence | undefined>(() => {
if (!tgsAnchor || !tgsWithinEvidenceWindow) return undefined;
const currentBody = spatialEvidence.frame?.bodyFrame;
const anchorBody = tgsReferenceEvidence.frame?.bodyFrame;
if (!currentBody || !anchorBody) return undefined;
return canonicalRecordedLabPackedTgsCells(tgsAnchor.costmap, anchorBody, currentBody);
}, [
spatialEvidence.frame?.bodyFrame,
tgsAnchor,
tgsReferenceEvidence.frame?.bodyFrame,
tgsWithinEvidenceWindow,
]);
const semanticOverlay = showCameraSemantic ? {
src: vegetationFullRouteMaskUrl(resultId, semanticLayer, sequenceIndex),
prefetchSrcs,
classes: semantic.classes,
palette: semantic.palette,
opacity: 0.46,
ariaLabel: `${layer.name} semantic prediction frame ${sequence}`,
} : undefined;
const mediaContent = (
<div className="m4-replay-threat-visual__media-layer" data-media={mediaMode ?? "none"}>
{videoSource ? (
<RecordedEvidenceVideoScene
source={videoSource}
playback={playbackController.playback}
imageWidth={review.width}
imageHeight={review.height}
boxes={[]}
semanticOverlay={semanticOverlay}
ariaLabel={`RAVNOVES004TREE recorded frame ${sequence}`}
interactive={false}
segmentSequence={sequence}
segmentCount={review.frameCount}
onPlaybackChange={playbackController.synchronize}
playbackAuthority="host"
playbackTransport="epoch-stream"
/>
) : (
<div className="l3-visual-audit__state" role={videoError ? "alert" : "status"}>
{videoError ?? "Открываем автономный RAVNOVES004TREE source…"}
</div>
)}
</div>
);
const spatialContent = spatialMode ? (
<>
{spatialEvidence.frame ? (
<LaboratoryMetricEvidenceScene
ref={metricSceneRef}
pointCloudBodyXyzM={showSourcePoints
? spatialEvidence.frame.sourcePointsBodyXyzM
: []}
localSurfaceBodyXyzM={showLocalSlam
? spatialEvidence.frame.localSlamBodyXyzM
: []}
obstacles={[]}
rig={{
lengthM: 1,
widthM: 0.8,
nominalSensorHeightM: spatialEvidence.frame.sensorHeight.meters,
}}
corridor={{ forwardLengthM: 12, rearMarginM: 1, halfWidthM: 0.4 }}
occupiedVoxelSizeM={tgsAnchor?.costmap.cellSizeM ?? 0.45}
mode={spatialMode}
label="RAV004 canonical source points, local SLAM and TGS costmap"
showCurrentIncrement={showSourcePoints}
showLocalSurface={showLocalSlam}
showRollingMap={showTgs}
showLowStep={false}
classifiedPackedCells={packedTgsCells}
classifiedCellSizeM={tgsAnchor?.costmap.cellSizeM}
showClassifiedCells={showTgs && Boolean(packedTgsCells)}
/>
) : (
<div className="l3-visual-audit__state" role={spatialEvidence.error ? "alert" : "status"}>
{spatialEvidence.loading ? <span className="busy-indicator" aria-hidden="true" /> : null}
<span>{spatialEvidence.error ?? "Открываем source points и Local SLAM из sealed RRD…"}</span>
</div>
)}
{showTgs && selectedTgsCase ? (
<div className="m4-replay-threat-visual__pane-status" role="status">
{tgsAnchorError ?? linkedReviewError ?? (tgsAnchorLoading
? `Открываем sealed TGS anchor ${selectedTgsCase.sourceSequence}; source/SLAM и общий clock продолжаются.`
: tgsWithinEvidenceWindow
? `TGS anchor ${selectedTgsCase.sourceSequence} из 10; source/SLAM и общий clock продолжаются.`
: `TGS anchor ${selectedTgsCase.sourceSequence} старше доказанного окна 1 с; слой скрыт, playback продолжается.`)}
</div>
) : null}
</>
) : null;
const mediaLayerControls = (
<div
className="m4-replay-threat-visual__pane-layer-controls"
role="group"
aria-label="Слои камеры и видео"
>
<Button
size="compact"
shape="pill"
variant={showCameraSemantic ? "primary" : "secondary"}
aria-pressed={showCameraSemantic}
onClick={() => setShowCameraSemantic((visible) => !visible)}
>
SEMANTICS
</Button>
<SegmentedControl
value={semanticLayer}
items={[...FULL_ROUTE_SEMANTIC_MODES]}
label="Источник семантики"
onChange={(value) => {
setSemanticLayer(value);
setShowCameraSemantic(true);
}}
/>
</div>
);
const spatialLayerControls = (
<div
className="m4-replay-threat-visual__pane-layer-controls"
role="group"
aria-label="Слои 3D и плана"
>
<Button
size="compact"
shape="pill"
variant={showSourcePoints ? "primary" : "secondary"}
aria-pressed={showSourcePoints}
onClick={() => setShowSourcePoints((visible) => !visible)}
>
SOURCE POINTS
</Button>
<Button
size="compact"
shape="pill"
variant={showLocalSlam ? "primary" : "secondary"}
aria-pressed={showLocalSlam}
onClick={() => setShowLocalSlam((visible) => !visible)}
>
LOCAL SLAM
</Button>
<Button
size="compact"
shape="pill"
variant={showTgs ? "primary" : "secondary"}
aria-pressed={showTgs}
disabled={!linkedReview}
title={linkedReviewError ?? "10 sealed causal TGS anchors; continuous TGS отсутствует"}
onClick={() => setShowTgs((visible) => !visible)}
>
TGS COSTMAP
</Button>
<Button
size="compact"
shape="pill"
variant={showCameraSemantic ? "primary" : "secondary"}
aria-pressed={showCameraSemantic}
title="Recorded semantic layer; camera-aligned prediction, без выдуманной 3D-проекции"
onClick={() => setShowCameraSemantic((visible) => !visible)}
>
SEMANTICS
</Button>
</div>
);
const resetSpatialView = (
<IconButton
label="Сбросить ракурс"
onClick={() => metricSceneRef.current?.resetView()}
>
<Icon name="refresh" size={16} />
</IconButton>
);
const overlayPanePercent = splitView && splitOrientation === "vertical"
? splitPrimarySize
: 100;
const overlay = (
<div
className="l3-visual-audit__overlay m4-replay-threat-visual__overlay"
style={{
"--m4-replay-threat-overlay-pane-width": `${overlayPanePercent}%`,
} as CSSProperties}
>
<div>
<span>RAVNOVES004TREE · recorded realtime</span>
<strong>frame {sequence}/{review.frameCount}</strong>
<small>
+{(playbackController.playback.currentSeconds - review.timelineStartSeconds).toFixed(3)} с
· {playbackController.playback.playing ? "воспроизведение" : "пауза / seek"}
</small>
</div>
<div>
<span>Spatial evidence</span>
<strong>{showTgs && selectedTgsCase && tgsWithinEvidenceWindow
? `TGS anchor ${selectedTgsCase.sourceSequence} · ${selectedTgsCase.tgs.occupiedCells} occupied`
: "source RRD · points + bounded Local SLAM"}</strong>
<small>{showTgs
? "TGS visible only inside sealed 1 s evidence window · playback retained"
: "5 s bounded Local SLAM · ground-rebased recorded source"}</small>
</div>
</div>
);
const transport = (
<ObservationTimeline
className="m4-replay-threat-visual__timeline"
active
sourceCount={4}
mode="recorded"
seekable
synchronization="host-arrival-best-effort"
rangeNs={{
min: Math.round(review.timelineStartSeconds * 1_000_000_000),
max: Math.round(review.timelineEndSeconds * 1_000_000_000),
}}
currentNs={Math.round(playbackController.playback.currentSeconds * 1_000_000_000)}
playing={playbackController.playback.playing}
playbackRate={playbackController.playback.rate ?? 1}
onSeek={(timeNs) => playbackController.seek(timeNs / 1_000_000_000)}
onPlayingChange={playbackController.setPlaying}
onPlaybackRateChange={playbackController.setRate}
showJumpToEnd={false}
/>
);
const semanticLayers = useMemo<readonly M4ReplayThreatSemanticLayer[]>(() => ([
{
id: "city",
controlLabel: "ГОРОД · EoMT",
resultId,
spatialResultId: null,
taxonomy: review.city.taxonomy,
maskUrl: (sequence) => vegetationFullRouteMaskUrl(resultId, "city", sequence),
label: review.city.name,
maskAriaLabel: "EoMT city semantic prediction",
},
{
id: "vegetation",
controlLabel: "ПРИРОДА · DDRNet",
resultId,
spatialResultId: null,
taxonomy: review.vegetation.taxonomy,
maskUrl: (sequence) => vegetationFullRouteMaskUrl(resultId, "vegetation", sequence),
label: review.vegetation.name,
maskAriaLabel: "DDRNet nature semantic prediction",
},
]), [resultId, review.city, review.vegetation]);
const sealedSpatialGap = useMemo<M4ReplayClassifiedSpatialLayer>(() => ({
label: "RAVNOVES004TREE",
pointLayerLabel: "SOURCE POINTS",
cellLayerLabel: "TGS COSTMAP",
cellLayerAvailable: false,
expectedAtSequence: false,
frame: null,
loading: false,
error: null,
replacePointCloud: false,
}), []);
return (
<CanonicalRecordedLabReplay
label="RAVNOVES004TREE full recorded review"
mediaMode={mediaMode ?? "none"}
mediaModes={FULL_ROUTE_MEDIA_MODES}
spatialMode={spatialMode ?? "none"}
spatialModes={FULL_ROUTE_SPATIAL_MODES}
expanded={expanded}
splitPrimarySize={splitPrimarySize}
splitOrientation={splitOrientation}
mediaAriaLabel={mediaMode === "camera" ? "Камера" : "Видео"}
spatialAriaLabel={spatialMode === "3d" ? "Трёхмерная сцена" : "Вид сверху"}
mediaLayerControls={mediaLayerControls}
spatialLayerControls={spatialLayerControls}
spatialLeadingControl={resetSpatialView}
mediaMultiLayer
mediaContent={mediaContent}
spatialContent={spatialContent}
emptyMessage="Выберите VIDEO/CAMERA или 3D/PLAN. Общий таймлайн останется на месте."
overlay={overlay}
transport={transport}
trailingActions={!splitView && spatialMode ? resetSpatialView : null}
onMediaModeChange={handleMediaModeChange}
onSpatialModeChange={handleSpatialModeChange}
onExpandedChange={setExpanded}
onSplitPrimarySizeChange={setSplitPrimarySize}
<M4ReplayThreatVisual
resultId={resultId}
timelineEndpointRoot={VEGETATION_TIMELINE_ENDPOINT}
semanticLayers={semanticLayers}
initialSemanticLayerId="vegetation"
initialSpatialMode="3d"
classifiedSpatialLayer={sealedSpatialGap}
evidenceLabel="RAVNOVES004TREE"
playbackTransport="segmented"
recoverTimestampStalls
showReferenceMediaLayers
showSpatialOverlaySummary
/>
);
}
@@ -575,32 +101,32 @@ function FullRouteReviewResult({
summary={(
<LaboratorySummary
title="LAB V1 · RAVNOVES004TREE · полный маршрут"
description="Общий recorded-LAB шаблон воспроизводит запись с травой и оврагами: RIGHT camera, исходное облако, ограниченный Local SLAM, два независимых semantic-слоя и десять реально просчитанных TGS-якорей."
description="Принятый recorded-LAB инструмент воспроизводит RAV004 без отдельного viewer: одна media-clock timeline, RIGHT camera, source points, bounded Local SLAM и переключаемые EoMT/DDRNet."
status="FULL RECORDED REVIEW · truth отсутствует · commands OFF"
statusTone="warning"
facts={[
{ label: "Источник", value: `${review.sourceId} · ${review.frameCount}/${review.frameCount} frames` },
{ label: "3D", value: "1437 source point chunks · 2825 SLAM poses · recorded RRD" },
{ label: "Источник", value: `${review.sourceId} · ${review.frameCount}/${review.frameCount} camera frames` },
{ label: "3D", value: "1444 source cloud increments · gravity-stable RFU → body" },
{ label: "Город", value: `${review.city.name} · ${decimal(review.city.inferenceFps, 2)} fps` },
{ label: "Природа", value: `${review.vegetation.name} · ${decimal(review.vegetation.inferenceFps, 2)} fps` },
{ label: "TGS", value: "10 sealed causal anchors · continuous costmap отсутствует" },
{ label: "TGS", value: "10 review anchors существуют · full-route artifact отсутствует" },
{ label: "Authority", value: `${rigLabel} · VISUAL REVIEW ONLY · commands OFF` },
]}
brief={{
question: "Что реально видно на полном RAV004-прогоне с высокой травой, оврагами и переходом к городу?",
approach: "Одна recorded timeline открывается общим LAB viewer. Camera и RRD синхронизированы; EoMT/DDRNet переключаются на камере, source points и 5-секундный Local SLAM — в 3D, TGS — только в доказанном окне десяти запечатанных якорей.",
principalResult: "RAV004 больше не подменяется RAV00: доступна полная исходная запись и её реальные пространственные слои.",
limitation: "Ручной truth, continuous TGS и point-aligned 3D semantics отсутствуют. Один повреждённый H.264-пакет на позиции 6092 заменён предыдущим декодированным кадром и отражён в proof.",
approach: "RAV004 поставляет только data/provider configuration в тот же M4 recorded viewer. Видеодекодер владеет clock; новые source increments проецируются в gravity-stable forward/left/up frame, Local SLAM ограничен пятью секундами.",
principalResult: "RAV004 больше не имеет отдельной логики окон, таймера, seek, cache или 3D controls. Модели и подписи меняются конфигурацией, архитектура переключения остаётся общей.",
limitation: "Full-route TGS, независимый person/vehicle detector, ручной truth и point-aligned 3D semantics пока не запечатаны. Semantic-derived рамки диагностические и не являются STOP-authority.",
}}
method={{
completeness: "complete",
executionClass: "ai-inference",
pipelineId: "ravnoves004tree-full-eomt-ddrnet-recorded-review/v1",
pipelineId: "canonical-recorded-lab-rav004tree/v3",
components: [
{ kind: "algorithm", name: "Recorded source points + bounded Local SLAM", version: "source-paced-ground-v2", role: "spatial source evidence", identitySha256: null },
{ kind: "algorithm", name: "Canonical recorded replay", version: "media-clock / one viewer", role: "shared camera + spatial transport", identitySha256: null },
{ kind: "algorithm", name: "Recorded source points + bounded Local SLAM", version: "source-paced-ground-v3", role: "gravity-stable spatial evidence", identitySha256: null },
{ kind: "model", name: review.city.name, version: "sealed Worker 006 run", role: "urban semantic review", identitySha256: null },
{ kind: "model", name: review.vegetation.name, version: "GOOSE DDRNet-39", role: "vegetation semantic review", identitySha256: null },
{ kind: "algorithm", name: "Causal TGS", version: "10 linked route anchors", role: "bounded geometric evidence", identitySha256: null },
],
}}
/>
@@ -617,19 +143,19 @@ function FullRouteReviewResult({
)}
result={(
<LaboratoryResultSummary
title="Полный RAV004 visual review восстановлен; управление не авторизовано"
status="Recorded evidence ready · navigation/actuation OFF"
title="RAV004 переведён на общий replay-каркас; safety evidence ещё не полно"
status="Recorded evidence · navigation/actuation OFF"
statusTone="warning"
metrics={[
{ label: "Route masks", value: "6830/6830 × 2", hint: "sealed local archives · Worker не требуется" },
{ label: "EoMT throughput", value: `${decimal(review.city.inferenceFps, 2)} fps`, hint: "изолированный полный прогон" },
{ label: "DDRNet throughput", value: `${decimal(review.vegetation.inferenceFps, 2)} fps`, hint: "изолированный полный прогон" },
{ label: "Spatial evidence", value: "RRD + 10 TGS anchors", hint: "continuous TGS и 3D semantics отсутствуют" },
{ label: "Camera timeline", value: "6830 frames · ≈9.51 Hz", hint: "media clock owns video, overlays and spatial" },
{ label: "Source geometry", value: "1444 increments · ≈2 Hz", hint: "last proven spatial frame is held between source arrivals" },
{ label: "EoMT throughput", value: `${decimal(review.city.inferenceFps, 2)} fps`, hint: "изолированный full pass; не realtime stack" },
{ label: "DDRNet throughput", value: `${decimal(review.vegetation.inferenceFps, 2)} fps`, hint: "изолированный full pass; temporal stability не принята" },
]}
conclusion={{
proved: "Полный RAV004 открывается в каноническом recorded viewer с camera, source points, bounded Local SLAM, EoMT, DDRNet и связанными TGS-якорями.",
notProved: "Не доказаны truth accuracy, временная стабильность DDRNet, continuous negative-obstacle detection и безопасное управление ровером.",
decision: "Использовать как visual audit. Следующий gate — truth-набор овраг/трава/дерево/яма и motion-aware temporal evaluation при целевых ≥10 FPS; navigation/actuation оставить OFF.",
proved: "Camera, seek, spatial layers and semantic switching use one accepted reusable viewer and one media clock; RFU source geometry no longer inherits LiDAR roll/pitch.",
notProved: "Не доказаны continuous TGS, независимый detector/STOP, truth accuracy, temporal stability DDRNet и ≥10 FPS совместного live stack.",
decision: "Продолжать как visual audit. До запечатанного full-route TGS и detector/load gate navigation/actuation остаются OFF.",
}}
/>
)}
@@ -754,27 +280,9 @@ export function VegetationShadowResultView({
executionClass: "ai-inference",
pipelineId: "ravnoves-eomt-ddrnet-yolox-causal-tgs-recorded-review/v1",
components: [
{
kind: "model",
name: "EoMT Cityscapes semantic",
version: "sealed E47 archive",
role: "urban semantic review",
identitySha256: null,
},
{
kind: "model",
name: selected.loadedModelName,
version: selected.candidate,
role: "vegetation material candidate",
identitySha256: selected.checkpointSha256,
},
{
kind: "algorithm",
name: "Frozen YOLOX + causal TGS",
version: "linked M4/M4.9 archives",
role: "independent object and geometry veto",
identitySha256: null,
},
{ kind: "model", name: "EoMT Cityscapes semantic", version: "sealed E47 archive", role: "urban semantic review", identitySha256: null },
{ kind: "model", name: selected.loadedModelName, version: selected.candidate, role: "vegetation material candidate", identitySha256: selected.checkpointSha256 },
{ kind: "algorithm", name: "Frozen YOLOX + causal TGS", version: "linked M4/M4.9 archives", role: "independent object and geometry veto", identitySha256: null },
],
}}
/>
@@ -795,26 +303,10 @@ export function VegetationShadowResultView({
status="Semantics advisory · YOLOX/TGS veto cannot be cleared"
statusTone="warning"
metrics={[
{
label: "Route masks",
value: `${route.frameCount}/${route.frameCount}`,
hint: "sealed local playback · Worker для открытия не нужен",
},
{
label: "Semantic sources",
value: "2 independent layers",
hint: "EoMT CITY / DDRNet VEGETATION · display switches, evidence does not fuse",
},
{
label: "Vegetation worker p95",
value: `${decimal(selected.shadowLatencyP95Ms, 2)} ms`,
hint: "изолированный DDRNet inference; не совместный realtime stack",
},
{
label: "Vegetation peak VRAM",
value: `${decimal(selected.peakReservedVramBytes / 1024 ** 3, 2)} GiB`,
hint: "DDRNet candidate на Worker 006",
},
{ label: "Route masks", value: `${route.frameCount}/${route.frameCount}`, hint: "sealed local playback · Worker для открытия не нужен" },
{ label: "Semantic sources", value: "2 independent layers", hint: "EoMT CITY / DDRNet VEGETATION · display switches, evidence does not fuse" },
{ label: "Vegetation worker p95", value: `${decimal(selected.shadowLatencyP95Ms, 2)} ms`, hint: "изолированный DDRNet inference; не совместный realtime stack" },
{ label: "Vegetation peak VRAM", value: `${decimal(selected.peakReservedVramBytes / 1024 ** 3, 2)} GiB`, hint: "DDRNet candidate на Worker 006" },
]}
conclusion={{
proved: "На одной recorded timeline доступны городской EoMT, природный DDRNet, YOLOX detections и causal TGS; LAB автономна от Worker.",
@@ -18,6 +18,7 @@ import {
const REQUESTED_CHUNK_FRAMES = 24;
const RETAINED_CHUNK_COUNT = 4;
const RETAINED_CHUNKS_BEHIND = 1;
const PREFETCH_CHUNKS_AHEAD = 1;
const RETAINED_CAMERA_POINT_OVERLAYS = 12;
@@ -31,10 +32,17 @@ export function m4ThreatChunkWindowStarts(
frameCount: number,
): readonly number[] {
if (chunkSize < 1 || frameCount < 1) return [];
return Array.from(
{ length: PREFETCH_CHUNKS_AHEAD + 1 },
(_, index) => activeChunkStart + index * chunkSize,
).filter((start) => start >= 0 && start < frameCount);
return [
activeChunkStart,
...Array.from(
{ length: RETAINED_CHUNKS_BEHIND },
(_, index) => activeChunkStart - (index + 1) * chunkSize,
),
...Array.from(
{ length: PREFETCH_CHUNKS_AHEAD },
(_, index) => activeChunkStart + (index + 1) * chunkSize,
),
].filter((start) => start >= 0 && start < frameCount);
}
export function cancelM4ThreatChunkRequestsOutsideWindow<T extends { abort(): void }>(