refactor(lab): canonicalize recorded spatial replay

This commit is contained in:
DCCONSTRUCTIONS
2026-08-29 23:38:17 +03:00
parent bd2892140f
commit 74da6437e9
12 changed files with 919 additions and 293 deletions
@@ -1,8 +1,11 @@
import { useEffect, useState } from "react";
import {
RecordedFmp4Player,
type RecordedObservationPlayback,
} from "../RecordedFmp4Player";
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
import type { RecordedCameraAdmissionState } from "../../core/observation/recordedSessionAdmission";
import {
RecordedEvidenceBoxOverlay,
type RecordedEvidenceBox,
@@ -33,6 +36,7 @@ export function RecordedEvidenceVideoScene({
segmentCount,
onPlaybackChange,
onPlayingRejected,
onAdmissionChange,
playbackAuthority = "media",
playbackTransport = "segmented",
}: {
@@ -49,9 +53,22 @@ export function RecordedEvidenceVideoScene({
segmentCount?: number;
onPlaybackChange?: (playback: RecordedObservationPlayback) => void;
onPlayingRejected?: () => void;
onAdmissionChange?: (state: RecordedCameraAdmissionState) => void;
playbackAuthority?: "media" | "host";
playbackTransport?: "segmented" | "epoch-stream";
}) {
const generation = source.delivery?.kind === "recorded-fmp4-manifest"
? source.delivery.manifestGenerationSha256
: "invalid";
const [admissionPhase, setAdmissionPhase] = useState<RecordedCameraAdmissionState["phase"]>(
"loading",
);
useEffect(() => setAdmissionPhase("loading"), [generation, source.id]);
const handleAdmissionChange = (next: RecordedCameraAdmissionState) => {
setAdmissionPhase(next.phase);
onAdmissionChange?.(next);
};
const sourceReady = admissionPhase === "ready";
return (
<div className="recorded-evidence-video-scene">
<RecordedFmp4Player
@@ -63,29 +80,32 @@ export function RecordedEvidenceVideoScene({
segmentCount={segmentCount}
onPlaybackChange={onPlaybackChange}
onPlayingRejected={onPlayingRejected}
onAdmissionChange={handleAdmissionChange}
playbackAuthority={playbackAuthority}
playbackTransport={playbackTransport}
/>
{semanticOverlay ? (
{sourceReady && semanticOverlay ? (
<RecordedEvidenceSemanticMaskOverlay
{...semanticOverlay}
imageWidth={imageWidth}
imageHeight={imageHeight}
/>
) : null}
{pointCloudOverlay ? (
{sourceReady && pointCloudOverlay ? (
<RecordedEvidencePointCloudOverlay
imageWidth={imageWidth}
imageHeight={imageHeight}
overlay={pointCloudOverlay}
/>
) : null}
<RecordedEvidenceBoxOverlay
imageWidth={imageWidth}
imageHeight={imageHeight}
boxes={boxes}
ariaLabel={ariaLabel}
/>
{sourceReady ? (
<RecordedEvidenceBoxOverlay
imageWidth={imageWidth}
imageHeight={imageHeight}
boxes={boxes}
ariaLabel={ariaLabel}
/>
) : null}
</div>
);
}
@@ -0,0 +1,116 @@
import { useEffect, useRef, useState } from "react";
import {
fetchCanonicalRecordedLabSpatialFrame,
type CanonicalRecordedLabSpatialFrame,
} from "../../core/laboratory/canonicalRecordedLabSpatial";
const FRAME_CACHE_LIMIT = 12;
/**
* Shared latest-request-wins scheduler for recorded LAB spatial evidence.
*
* A feature supplies only the sealed session identity and host-clock time.
* Cache ownership, identity fencing and stale-response suppression remain in
* the canonical instrument instead of being reimplemented per experiment.
*/
export function useCanonicalRecordedLabSpatialFrame({
sessionId,
generationSha256,
targetTimeNs,
}: {
sessionId: string;
generationSha256: string | null;
targetTimeNs: number;
}) {
const [frame, setFrame] = useState<CanonicalRecordedLabSpatialFrame | null>(null);
const [error, setError] = useState<string | null>(null);
const desiredRef = useRef<number | null>(null);
const runningRef = useRef(false);
const mountedRef = useRef(true);
const identityRef = useRef("");
const cacheRef = useRef(new Map<number, CanonicalRecordedLabSpatialFrame>());
const pumpRef = useRef<() => void>(() => undefined);
const identity = `${sessionId}:${generationSha256 ?? "unavailable"}`;
identityRef.current = identity;
pumpRef.current = () => {
if (runningRef.current || desiredRef.current === null || !generationSha256) return;
runningRef.current = true;
const requestIdentity = identity;
let settledTimeNs: number | null = null;
void (async () => {
while (
mountedRef.current
&& identityRef.current === requestIdentity
&& desiredRef.current !== null
) {
const requestedTimeNs = desiredRef.current;
const cached = cacheRef.current.get(requestedTimeNs);
try {
const next = cached ?? await fetchCanonicalRecordedLabSpatialFrame(
sessionId,
generationSha256,
requestedTimeNs,
);
if (!mountedRef.current || identityRef.current !== requestIdentity) break;
if (!cached) {
cacheRef.current.set(requestedTimeNs, next);
while (cacheRef.current.size > FRAME_CACHE_LIMIT) {
const oldest = cacheRef.current.keys().next().value as number | undefined;
if (oldest === undefined) break;
cacheRef.current.delete(oldest);
}
}
setFrame(next);
setError(null);
} catch (caught: unknown) {
if (!mountedRef.current || identityRef.current !== requestIdentity) break;
setError(caught instanceof Error
? caught.message
: "Spatial-слои записанной LAB недоступны.");
}
settledTimeNs = requestedTimeNs;
if (desiredRef.current === requestedTimeNs) break;
}
})().finally(() => {
runningRef.current = false;
if (
mountedRef.current
&& desiredRef.current !== null
&& (identityRef.current !== requestIdentity || desiredRef.current !== settledTimeNs)
) {
pumpRef.current();
}
});
};
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
desiredRef.current = null;
};
}, []);
useEffect(() => {
cacheRef.current.clear();
desiredRef.current = null;
setFrame(null);
setError(null);
}, [identity]);
useEffect(() => {
if (!generationSha256) return;
desiredRef.current = targetTimeNs;
const cached = cacheRef.current.get(targetTimeNs);
if (cached) {
setFrame(cached);
setError(null);
return;
}
pumpRef.current();
}, [generationSha256, identity, targetTimeNs]);
return { frame, error, loading: Boolean(generationSha256) && !frame && !error };
}
@@ -0,0 +1,99 @@
export const CANONICAL_RECORDED_LAB_TGS_HISTORY_SECONDS = 1;
export const CANONICAL_RECORDED_LAB_SPATIAL_PROFILE = "source-paced-ground-v2";
export interface CanonicalRecordedLabPackedCellEvidence {
centersBodyXyM: Float32Array;
zBoundsM: Float32Array;
stateCodes: Uint8Array;
}
export interface CanonicalRecordedLabBodyGroundFrame {
originMapXyzM: readonly [number, number, number];
sensorOriginMapXyzM: readonly [number, number, number];
basisMapFromBody: readonly [
readonly [number, number, number],
readonly [number, number, number],
readonly [number, number, number],
];
}
export interface CanonicalRecordedLabTgsCostmap {
centersXyM: readonly (readonly [number, number])[];
stateCodes: readonly number[];
zBoundsM: readonly (readonly [number | null, number | null])[];
}
export function canonicalRecordedLabTgsIsCurrent(
currentTimeNs: number,
anchorTimeNs: number,
historySeconds = CANONICAL_RECORDED_LAB_TGS_HISTORY_SECONDS,
): boolean {
if (
!Number.isSafeInteger(currentTimeNs)
|| !Number.isSafeInteger(anchorTimeNs)
|| !Number.isFinite(historySeconds)
|| historySeconds <= 0
) return false;
const ageNs = currentTimeNs - anchorTimeNs;
return ageNs >= 0 && ageNs <= Math.round(historySeconds * 1_000_000_000);
}
export function canonicalMapGravityLocalPointToBodyGround(
point: readonly [number, number, number],
anchor: CanonicalRecordedLabBodyGroundFrame,
current: CanonicalRecordedLabBodyGroundFrame,
): readonly [number, number, number] {
// TGS is translation-only map-gravity-local: its axes are map axes and its
// origin is the LiDAR at the source frame. It is not an anchor body frame.
const map: readonly [number, number, number] = [
anchor.sensorOriginMapXyzM[0] + point[0],
anchor.sensorOriginMapXyzM[1] + point[1],
anchor.sensorOriginMapXyzM[2] + point[2],
];
const delta: readonly [number, number, number] = [
map[0] - current.originMapXyzM[0],
map[1] - current.originMapXyzM[1],
map[2] - current.originMapXyzM[2],
];
return [
current.basisMapFromBody[0][0] * delta[0]
+ current.basisMapFromBody[1][0] * delta[1]
+ current.basisMapFromBody[2][0] * delta[2],
current.basisMapFromBody[0][1] * delta[0]
+ current.basisMapFromBody[1][1] * delta[1]
+ current.basisMapFromBody[2][1] * delta[2],
current.basisMapFromBody[0][2] * delta[0]
+ current.basisMapFromBody[1][2] * delta[1]
+ current.basisMapFromBody[2][2] * delta[2],
];
}
export function canonicalRecordedLabPackedTgsCells(
costmap: CanonicalRecordedLabTgsCostmap,
anchor: CanonicalRecordedLabBodyGroundFrame,
current: CanonicalRecordedLabBodyGroundFrame,
): CanonicalRecordedLabPackedCellEvidence {
if (
costmap.centersXyM.length !== costmap.stateCodes.length
|| costmap.centersXyM.length !== costmap.zBoundsM.length
) throw new Error("Canonical recorded LAB TGS accounting changed");
const centers: number[] = [];
const zBounds: number[] = [];
costmap.centersXyM.forEach(([x, y], index) => {
const bounds = costmap.zBoundsM[index] ?? [null, null];
const center = canonicalMapGravityLocalPointToBodyGround([x, y, 0], anchor, current);
centers.push(center[0], center[1]);
if (bounds[0] === null || bounds[1] === null) {
zBounds.push(Number.NaN, Number.NaN);
return;
}
const bottom = canonicalMapGravityLocalPointToBodyGround([x, y, bounds[0]], anchor, current);
const top = canonicalMapGravityLocalPointToBodyGround([x, y, bounds[1]], anchor, current);
zBounds.push(Math.min(bottom[2], top[2]), Math.max(bottom[2], top[2]));
});
return {
centersBodyXyM: Float32Array.from(centers),
zBoundsM: Float32Array.from(zBounds),
stateCodes: Uint8Array.from(costmap.stateCodes),
};
}
@@ -0,0 +1,256 @@
import type { LaboratoryFetch } from "./advancedResults";
import { CANONICAL_RECORDED_LAB_SPATIAL_PROFILE } from "./canonicalRecordedLab";
const SAFE_SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const SHA256 = /^[a-f0-9]{64}$/;
export class CanonicalRecordedLabSpatialContractError extends Error {}
export interface CanonicalRecordedLabSpatialFrame {
targetTimeNs: number;
sourceTimeNs: number;
poseTimeNs: number;
trajectoryTimeNs: number;
sourcePointCount: number;
coordinateFrame: "body-ground";
sensorHeight: {
meters: number;
source: "initial-source-cloud-lower-quantile-median";
sampleCount: number;
madM: number;
authority: "visual-derived";
};
spatialProfile: {
profileId: typeof CANONICAL_RECORDED_LAB_SPATIAL_PROFILE;
localSlamHistorySeconds: number;
localSlamRadiusM: number;
localSlamVoxelSizeM: number;
localSlamPointLimit: number;
};
bodyFrame: {
originMapXyzM: readonly [number, number, number];
sensorOriginMapXyzM: readonly [number, number, number];
basisMapFromBody: readonly [
readonly [number, number, number],
readonly [number, number, number],
readonly [number, number, number],
];
};
sourcePointsBodyXyzM: readonly (readonly [number, number, number])[];
localSlamSourceFrameCount: number;
localSlamSourcePointCount: number;
localSlamBodyXyzM: readonly (readonly [number, number, number])[];
}
function objectValue(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function arrayValue(value: unknown, label: string): unknown[] {
if (!Array.isArray(value)) {
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидался массив.`);
}
return value;
}
function exact(value: unknown, expected: unknown, label: string): void {
if (value !== expected) {
throw new CanonicalRecordedLabSpatialContractError(`${label}: контракт изменён.`);
}
}
function numberValue(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидалось число.`);
}
return value;
}
function integerValue(value: unknown, label: string): number {
const parsed = numberValue(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидалось целое значение.`);
}
return parsed;
}
function pointList(
value: unknown,
label: string,
): readonly (readonly [number, number, number])[] {
return arrayValue(value, label).map((entry, index) => {
const point = arrayValue(entry, `${label}[${index}]`).map(
(channel, channelIndex) => numberValue(channel, `${label}[${index}][${channelIndex}]`),
);
if (point.length !== 3) {
throw new CanonicalRecordedLabSpatialContractError(`${label}[${index}]: размер изменён.`);
}
return [point[0]!, point[1]!, point[2]!] as const;
});
}
export async function fetchCanonicalRecordedLabSpatialFrame(
sessionId: string,
generationSha256: string,
targetTimeNs: number,
{
fetcher = fetch,
signal,
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<CanonicalRecordedLabSpatialFrame> {
if (
!SAFE_SESSION_ID.test(sessionId)
|| !SHA256.test(generationSha256)
|| !Number.isSafeInteger(targetTimeNs)
|| targetTimeNs < 0
) {
throw new CanonicalRecordedLabSpatialContractError(
"Canonical LAB spatial identity недопустима.",
);
}
const query = new URLSearchParams({
generation: generationSha256,
time_ns: String(targetTimeNs),
profile: CANONICAL_RECORDED_LAB_SPATIAL_PROFILE,
});
const response = await fetcher(
`/api/v1/observation-sessions/${encodeURIComponent(sessionId)}`
+ `/canonical-lab/spatial-frame?${query.toString()}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new CanonicalRecordedLabSpatialContractError(
`Canonical LAB spatial frame недоступен: HTTP ${response.status}.`,
);
}
const payload = objectValue(await response.json(), "canonical_lab.spatial_frame");
exact(
payload.schema_version,
"missioncore.canonical-recorded-lab-spatial-frame/v2",
"canonical_lab.spatial_frame.schema_version",
);
exact(payload.coordinate_frame, "body-ground", "canonical_lab.spatial_frame.coordinate_frame");
exact(payload.target_time_ns, targetTimeNs, "canonical_lab.spatial_frame.target_time_ns");
const sourcePoints = pointList(
payload.source_points_body_xyz_m,
"canonical_lab.spatial_frame.source_points",
);
const localSlam = pointList(
payload.local_slam_body_xyz_m,
"canonical_lab.spatial_frame.local_slam",
);
const sourcePointCount = integerValue(
payload.source_point_count,
"canonical_lab.spatial_frame.source_point_count",
);
const localSlamPointCount = integerValue(
payload.local_slam_point_count,
"canonical_lab.spatial_frame.local_slam_point_count",
);
if (
sourcePointCount !== sourcePoints.length
|| sourcePointCount > 100_000
|| localSlamPointCount !== localSlam.length
|| localSlam.length > 27_000
) {
throw new CanonicalRecordedLabSpatialContractError(
"Canonical LAB spatial accounting изменён.",
);
}
const bodyFrame = objectValue(payload.body_frame, "canonical_lab.spatial_frame.body_frame");
const origin = pointList(
[bodyFrame.origin_map_xyz_m],
"canonical_lab.spatial_frame.body_frame.origin",
)[0]!;
const sensorOrigin = pointList(
[bodyFrame.sensor_origin_map_xyz_m],
"canonical_lab.spatial_frame.body_frame.sensor_origin",
)[0]!;
const basisRows = pointList(
bodyFrame.basis_map_from_body,
"canonical_lab.spatial_frame.body_frame.basis",
);
if (basisRows.length !== 3) {
throw new CanonicalRecordedLabSpatialContractError(
"Canonical LAB spatial basis изменён.",
);
}
const sensorHeight = objectValue(payload.sensor_height, "canonical_lab.spatial_frame.sensor_height");
exact(
sensorHeight.source,
"initial-source-cloud-lower-quantile-median",
"canonical_lab.spatial_frame.sensor_height.source",
);
exact(
sensorHeight.authority,
"visual-derived",
"canonical_lab.spatial_frame.sensor_height.authority",
);
const spatialProfile = objectValue(
payload.spatial_profile,
"canonical_lab.spatial_frame.spatial_profile",
);
exact(
spatialProfile.profile_id,
CANONICAL_RECORDED_LAB_SPATIAL_PROFILE,
"canonical_lab.spatial_frame.spatial_profile.profile_id",
);
return {
targetTimeNs,
sourceTimeNs: integerValue(payload.source_time_ns, "canonical_lab.spatial_frame.source_time_ns"),
poseTimeNs: integerValue(payload.pose_time_ns, "canonical_lab.spatial_frame.pose_time_ns"),
trajectoryTimeNs: integerValue(
payload.trajectory_time_ns,
"canonical_lab.spatial_frame.trajectory_time_ns",
),
sourcePointCount,
coordinateFrame: "body-ground",
sensorHeight: {
meters: numberValue(sensorHeight.meters, "canonical_lab.spatial_frame.sensor_height.meters"),
source: "initial-source-cloud-lower-quantile-median",
sampleCount: integerValue(
sensorHeight.sample_count,
"canonical_lab.spatial_frame.sensor_height.sample_count",
),
madM: numberValue(sensorHeight.mad_m, "canonical_lab.spatial_frame.sensor_height.mad_m"),
authority: "visual-derived",
},
spatialProfile: {
profileId: CANONICAL_RECORDED_LAB_SPATIAL_PROFILE,
localSlamHistorySeconds: numberValue(
spatialProfile.local_slam_history_seconds,
"canonical_lab.spatial_frame.spatial_profile.history",
),
localSlamRadiusM: numberValue(
spatialProfile.local_slam_radius_m,
"canonical_lab.spatial_frame.spatial_profile.radius",
),
localSlamVoxelSizeM: numberValue(
spatialProfile.local_slam_voxel_size_m,
"canonical_lab.spatial_frame.spatial_profile.voxel",
),
localSlamPointLimit: integerValue(
spatialProfile.local_slam_point_limit,
"canonical_lab.spatial_frame.spatial_profile.limit",
),
},
bodyFrame: {
originMapXyzM: origin,
sensorOriginMapXyzM: sensorOrigin,
basisMapFromBody: [basisRows[0]!, basisRows[1]!, basisRows[2]!],
},
sourcePointsBodyXyzM: sourcePoints,
localSlamSourceFrameCount: integerValue(
payload.local_slam_source_frame_count,
"canonical_lab.spatial_frame.local_slam_source_frames",
),
localSlamSourcePointCount: integerValue(
payload.local_slam_source_point_count,
"canonical_lab.spatial_frame.local_slam_source_points",
),
localSlamBodyXyzM: localSlam,
};
}
@@ -118,24 +118,6 @@ export interface VegetationRouteTgsAnchor {
};
}
export interface CanonicalRecordedLabSpatialFrame {
targetTimeNs: number;
sourceTimeNs: number;
poseTimeNs: number;
trajectoryTimeNs: number;
sourcePointCount: number;
bodyFrame: {
originMapXyzM: readonly [number, number, number];
basisMapFromBody: readonly [
readonly [number, number, number],
readonly [number, number, number],
readonly [number, number, number],
];
};
sourcePointsBodyXyzM: readonly (readonly [number, number, number])[];
localSlamBodyXyzM: readonly (readonly [number, number, number])[];
}
export interface VegetationFullRouteLayer {
name: string;
resultId: string;
@@ -1044,100 +1026,6 @@ export async function fetchVegetationRouteTgsAnchor(
};
}
export async function fetchCanonicalRecordedLabSpatialFrame(
sessionId: string,
generationSha256: string,
targetTimeNs: number,
{
fetcher = fetch,
signal,
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<CanonicalRecordedLabSpatialFrame> {
if (
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(sessionId)
|| !SHA256.test(generationSha256)
|| !Number.isSafeInteger(targetTimeNs)
|| targetTimeNs < 0
) {
throw new VegetationShadowContractError("Canonical LAB spatial identity недопустима.");
}
const query = new URLSearchParams({
generation: generationSha256,
time_ns: String(targetTimeNs),
});
const response = await fetcher(
`/api/v1/observation-sessions/${encodeURIComponent(sessionId)}`
+ `/canonical-lab/spatial-frame?${query.toString()}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new VegetationShadowContractError(
`Canonical LAB spatial frame недоступен: HTTP ${response.status}.`,
);
}
const payload = objectValue(await response.json(), "canonical_lab.spatial_frame");
exact(
payload.schema_version,
"missioncore.canonical-recorded-lab-spatial-frame/v1",
"canonical_lab.spatial_frame.schema_version",
);
exact(payload.target_time_ns, targetTimeNs, "canonical_lab.spatial_frame.target_time_ns");
const pointList = (value: unknown, label: string) => arrayValue(value, label).map(
(entry, index) => {
const point = arrayValue(entry, `${label}[${index}]`).map(
(channel, channelIndex) => numberValue(channel, `${label}[${index}][${channelIndex}]`),
);
if (point.length !== 3) {
throw new VegetationShadowContractError(`${label}[${index}]: размер изменён.`);
}
return [point[0]!, point[1]!, point[2]!] as const;
},
);
const sourcePoints = pointList(
payload.source_points_body_xyz_m,
"canonical_lab.spatial_frame.source_points",
);
const localSlam = pointList(
payload.local_slam_body_xyz_m,
"canonical_lab.spatial_frame.local_slam",
);
const sourcePointCount = integerValue(
payload.source_point_count,
"canonical_lab.spatial_frame.source_point_count",
);
if (sourcePointCount !== sourcePoints.length || sourcePointCount > 100_000 || localSlam.length > 10_000) {
throw new VegetationShadowContractError("Canonical LAB spatial accounting изменён.");
}
const bodyFrame = objectValue(payload.body_frame, "canonical_lab.spatial_frame.body_frame");
const origin = pointList(
[bodyFrame.origin_map_xyz_m],
"canonical_lab.spatial_frame.body_frame.origin",
)[0]!;
const basisRows = pointList(
bodyFrame.basis_map_from_body,
"canonical_lab.spatial_frame.body_frame.basis",
);
if (basisRows.length !== 3) {
throw new VegetationShadowContractError("Canonical LAB spatial basis изменён.");
}
return {
targetTimeNs,
sourceTimeNs: integerValue(payload.source_time_ns, "canonical_lab.spatial_frame.source_time_ns"),
poseTimeNs: integerValue(payload.pose_time_ns, "canonical_lab.spatial_frame.pose_time_ns"),
trajectoryTimeNs: integerValue(
payload.trajectory_time_ns,
"canonical_lab.spatial_frame.trajectory_time_ns",
),
sourcePointCount,
bodyFrame: {
originMapXyzM: origin,
basisMapFromBody: [basisRows[0]!, basisRows[1]!, basisRows[2]!],
},
sourcePointsBodyXyzM: sourcePoints,
localSlamBodyXyzM: localSlam,
};
}
export async function fetchVegetationShadowResult(
resultId: string,
{
@@ -23,6 +23,7 @@ import {
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,
@@ -35,12 +36,14 @@ import {
type RecordedEvidenceSemanticPaletteEntry,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay";
import {
fetchCanonicalRecordedLabSpatialFrame,
canonicalRecordedLabPackedTgsCells,
canonicalRecordedLabTgsIsCurrent,
} from "../../core/laboratory/canonicalRecordedLab";
import {
fetchVegetationShadowResult,
fetchVegetationRouteTgsAnchor,
vegetationFullRouteMaskUrl,
vegetationVideoMaskUrl,
type CanonicalRecordedLabSpatialFrame,
type VegetationFullRouteLayer,
type VegetationFullRouteReview,
type VegetationMixedRouteCase,
@@ -105,9 +108,7 @@ function causalTgsCase(
&& (!latest || candidate.sourceSequence > latest.sourceSequence)
? candidate
: latest
), null) ?? cases.reduce((first, candidate) => (
candidate.sourceSequence < first.sourceSequence ? candidate : first
));
), null);
}
function nearestFullRouteFrameIndex(
@@ -130,92 +131,6 @@ function nearestFullRouteFrameIndex(
: low;
}
function useCanonicalRavSpatialFrame(
review: VegetationFullRouteReview,
replayLaunch: ObservationSessionReplayLaunch | null,
targetTimeNs: number,
) {
const [frame, setFrame] = useState<CanonicalRecordedLabSpatialFrame | null>(null);
const [error, setError] = useState<string | null>(null);
const desiredRef = useRef<number | null>(null);
const runningRef = useRef(false);
const mountedRef = useRef(true);
const cacheRef = useRef(new Map<number, CanonicalRecordedLabSpatialFrame>());
const pumpRef = useRef<() => void>(() => undefined);
pumpRef.current = () => {
if (runningRef.current || desiredRef.current === null || !replayLaunch) return;
runningRef.current = true;
let settledTimeNs: number | null = null;
void (async () => {
while (mountedRef.current && desiredRef.current !== null) {
const requestedTimeNs = desiredRef.current;
const cached = cacheRef.current.get(requestedTimeNs);
try {
const next = cached ?? await fetchCanonicalRecordedLabSpatialFrame(
review.sessionId,
replayLaunch.sha256,
requestedTimeNs,
);
if (!cached) {
cacheRef.current.set(requestedTimeNs, next);
while (cacheRef.current.size > 12) {
const oldest = cacheRef.current.keys().next().value as number | undefined;
if (oldest === undefined) break;
cacheRef.current.delete(oldest);
}
}
if (!mountedRef.current) break;
setFrame(next);
setError(null);
} catch (caught: unknown) {
if (!mountedRef.current) break;
setError(caught instanceof Error ? caught.message : "Spatial-слои RAV004 недоступны.");
}
settledTimeNs = requestedTimeNs;
if (desiredRef.current === requestedTimeNs) break;
}
})().finally(() => {
runningRef.current = false;
if (
mountedRef.current
&& desiredRef.current !== null
&& desiredRef.current !== settledTimeNs
) {
pumpRef.current();
}
});
};
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
desiredRef.current = null;
};
}, []);
useEffect(() => {
cacheRef.current.clear();
setFrame(null);
setError(null);
}, [replayLaunch?.sha256, review.sessionId]);
useEffect(() => {
if (!replayLaunch) return;
desiredRef.current = targetTimeNs;
const cached = cacheRef.current.get(targetTimeNs);
if (cached) {
setFrame(cached);
setError(null);
return;
}
pumpRef.current();
}, [replayLaunch, targetTimeNs]);
return { frame, error, loading: Boolean(replayLaunch) && !frame && !error };
}
function FullRouteReviewEvidence({
resultId,
review,
@@ -266,7 +181,11 @@ function FullRouteReviewEvidence({
const spatialRequestTimeNs = review.frameSourceTimesNs[spatialRequestIndex]
?? review.frameSourceTimesNs[sequenceIndex]
?? Math.round(playbackController.playback.currentSeconds * 1_000_000_000);
const spatialEvidence = useCanonicalRavSpatialFrame(review, replayLaunch, spatialRequestTimeNs);
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
@@ -343,11 +262,17 @@ function FullRouteReviewEvidence({
? review.frameSourceTimesNs[selectedTgsCase.sourceSequence - 1]
?? Math.round(selectedTgsCase.sessionSeconds * 1_000_000_000)
: spatialRequestTimeNs;
const tgsReferenceEvidence = useCanonicalRavSpatialFrame(
review,
replayLaunch,
selectedTgsTimeNs,
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) {
@@ -376,53 +301,24 @@ function FullRouteReviewEvidence({
}, [review.linkedRouteReviewResultId, selectedTgsCase?.sourceSequence, showTgs]);
const packedTgsCells = useMemo<LaboratoryMetricPackedCellEvidence | undefined>(() => {
if (!tgsAnchor) return undefined;
if (!tgsAnchor || !tgsWithinEvidenceWindow) return undefined;
const currentBody = spatialEvidence.frame?.bodyFrame;
const anchorBody = tgsReferenceEvidence.frame?.bodyFrame;
const transformPoint = (point: readonly [number, number, number]) => {
if (!currentBody || !anchorBody) return point;
const map = [0, 1, 2].map((row) => (
anchorBody.originMapXyzM[row]!
+ anchorBody.basisMapFromBody[row]!.reduce(
(sum, coefficient, column) => sum + coefficient * point[column]!,
0,
)
));
const delta = map.map((value, index) => value - currentBody.originMapXyzM[index]!);
return [0, 1, 2].map((column) => (
currentBody.basisMapFromBody.reduce(
(sum, row, rowIndex) => sum + row[column]! * delta[rowIndex]!,
0,
)
)) as [number, number, number];
};
const centers: number[] = [];
const zBounds: number[] = [];
tgsAnchor.costmap.centersXyM.forEach(([x, y], index) => {
const bounds = tgsAnchor.costmap.zBoundsM[index] ?? [null, null];
const center = transformPoint([x, y, 0]);
centers.push(center[0], center[1]);
if (bounds[0] === null || bounds[1] === null) {
zBounds.push(Number.NaN, Number.NaN);
} else {
const bottom = transformPoint([x, y, bounds[0]]);
const top = transformPoint([x, y, bounds[1]]);
zBounds.push(Math.min(bottom[2], top[2]), Math.max(bottom[2], top[2]));
}
});
return {
centersBodyXyM: Float32Array.from(centers),
zBoundsM: Float32Array.from(zBounds),
stateCodes: Uint8Array.from(tgsAnchor.costmap.stateCodes),
};
}, [spatialEvidence.frame?.bodyFrame, tgsAnchor, 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.76,
opacity: 0.46,
ariaLabel: `${layer.name} semantic prediction frame ${sequence}`,
} : undefined;
@@ -464,7 +360,11 @@ function FullRouteReviewEvidence({
? spatialEvidence.frame.localSlamBodyXyzM
: []}
obstacles={[]}
rig={{ lengthM: 1, widthM: 0.8, nominalSensorHeightM: 0.4 }}
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}
@@ -487,7 +387,9 @@ function FullRouteReviewEvidence({
<div className="m4-replay-threat-visual__pane-status" role="status">
{tgsAnchorError ?? linkedReviewError ?? (tgsAnchorLoading
? `Открываем sealed TGS anchor ${selectedTgsCase.sourceSequence}; source/SLAM и общий clock продолжаются.`
: `TGS anchor ${selectedTgsCase.sourceSequence} из 10; source/SLAM и общий clock продолжаются.`)}
: tgsWithinEvidenceWindow
? `TGS anchor ${selectedTgsCase.sourceSequence} из 10; source/SLAM и общий clock продолжаются.`
: `TGS anchor ${selectedTgsCase.sourceSequence} старше доказанного окна 1 с; слой скрыт, playback продолжается.`)}
</div>
) : null}
</>
@@ -597,12 +499,12 @@ function FullRouteReviewEvidence({
</div>
<div>
<span>Spatial evidence</span>
<strong>{showTgs && selectedTgsCase
<strong>{showTgs && selectedTgsCase && tgsWithinEvidenceWindow
? `TGS anchor ${selectedTgsCase.sourceSequence} · ${selectedTgsCase.tgs.occupiedCells} occupied`
: "source RRD · points + SLAM"}</strong>
: "source RRD · points + bounded Local SLAM"}</strong>
<small>{showTgs
? "latest causal of 10 sealed anchors · continuous playback retained"
: "causal 1 s view · grayscale intensity · recorded source identity"}</small>
? "TGS visible only inside sealed 1 s evidence window · playback retained"
: "5 s bounded Local SLAM · ground-rebased recorded source"}</small>
</div>
</div>
);
@@ -673,7 +575,7 @@ function FullRouteReviewResult({
summary={(
<LaboratorySummary
title="LAB V1 · RAVNOVES004TREE · полный маршрут"
description="Общий recorded-LAB шаблон воспроизводит запись с травой и оврагами: RIGHT camera, исходное облако, SLAM trajectory, два независимых semantic-слоя и десять реально просчитанных TGS-якорей."
description="Общий recorded-LAB шаблон воспроизводит запись с травой и оврагами: RIGHT camera, исходное облако, ограниченный Local SLAM, два независимых semantic-слоя и десять реально просчитанных TGS-якорей."
status="FULL RECORDED REVIEW · truth отсутствует · commands OFF"
statusTone="warning"
facts={[
@@ -686,7 +588,7 @@ function FullRouteReviewResult({
]}
brief={{
question: "Что реально видно на полном RAV004-прогоне с высокой травой, оврагами и переходом к городу?",
approach: "Одна recorded timeline открывается общим LAB viewer. Camera и RRD синхронизированы; EoMT/DDRNet переключаются на камере, source points и SLAM trajectory — в 3D, TGS — только на десяти запечатанных якорях.",
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.",
}}
@@ -695,7 +597,7 @@ function FullRouteReviewResult({
executionClass: "ai-inference",
pipelineId: "ravnoves004tree-full-eomt-ddrnet-recorded-review/v1",
components: [
{ kind: "algorithm", name: "Recorded source points + SLAM trajectory", version: "sealed RRD", role: "spatial source evidence", identitySha256: null },
{ kind: "algorithm", name: "Recorded source points + bounded Local SLAM", version: "source-paced-ground-v2", role: "spatial source 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 },
@@ -725,7 +627,7 @@ function FullRouteReviewResult({
{ label: "Spatial evidence", value: "RRD + 10 TGS anchors", hint: "continuous TGS и 3D semantics отсутствуют" },
]}
conclusion={{
proved: "Полный RAV004 открывается в каноническом recorded viewer с camera, source points, SLAM trajectory, EoMT, DDRNet и связанными TGS-якорями.",
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.",
}}
@@ -0,0 +1,78 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let canonicalMapGravityLocalPointToBodyGround;
let canonicalRecordedLabPackedTgsCells;
let canonicalRecordedLabTgsIsCurrent;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({
canonicalMapGravityLocalPointToBodyGround,
canonicalRecordedLabPackedTgsCells,
canonicalRecordedLabTgsIsCurrent,
} = await server.ssrLoadModule("/src/core/laboratory/canonicalRecordedLab.ts"));
});
after(async () => {
await server?.close();
});
const identity = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
];
test("canonical TGS validity never retains a sparse anchor beyond its sealed history", () => {
assert.equal(canonicalRecordedLabTgsIsCurrent(2_000_000_000, 1_000_000_000), true);
assert.equal(canonicalRecordedLabTgsIsCurrent(2_000_000_001, 1_000_000_000), false);
assert.equal(canonicalRecordedLabTgsIsCurrent(999_999_999, 1_000_000_000), false);
});
test("map-gravity-local TGS uses sensor translation and current ground body exactly once", () => {
const anchor = {
originMapXyzM: [10, 20, 0.68],
sensorOriginMapXyzM: [10, 20, 1],
basisMapFromBody: identity,
};
const current = {
originMapXyzM: [8, 20, 0],
sensorOriginMapXyzM: [8, 20, 0.32],
basisMapFromBody: identity,
};
assert.deepEqual(
canonicalMapGravityLocalPointToBodyGround([1, 2, -1], anchor, current),
[3, 2, 0],
);
const packed = canonicalRecordedLabPackedTgsCells({
centersXyM: [[1, 2]],
stateCodes: [2],
zBoundsM: [[-1, 0]],
}, anchor, current);
assert.deepEqual([...packed.centersBodyXyM], [3, 2]);
assert.deepEqual([...packed.zBoundsM], [0, 1]);
assert.deepEqual([...packed.stateCodes], [2]);
});
test("recorded LAB spatial loading is shared, profile-bound and experiment-neutral", async () => {
const [contract, scheduler, vegetation] = await Promise.all([
readFile(new URL("../src/core/laboratory/canonicalRecordedLabSpatial.ts", import.meta.url), "utf8"),
readFile(new URL("../src/components/laboratory/useCanonicalRecordedLabSpatialFrame.ts", import.meta.url), "utf8"),
readFile(new URL("../src/core/laboratory/vegetationShadow.ts", import.meta.url), "utf8"),
]);
assert.match(contract, /CANONICAL_RECORDED_LAB_SPATIAL_PROFILE/);
assert.match(contract, /profile: CANONICAL_RECORDED_LAB_SPATIAL_PROFILE/);
assert.match(scheduler, /Shared latest-request-wins scheduler/);
assert.match(scheduler, /identityRef\.current !== requestIdentity/);
assert.doesNotMatch(scheduler, /RAVNOVES|vegetation|DDRNet/);
assert.doesNotMatch(vegetation, /fetchCanonicalRecordedLabSpatialFrame|CanonicalRecordedLabSpatialFrame/);
});
@@ -19,13 +19,15 @@ before(async () => {
});
({
fetchVegetationBenchmarkResult,
fetchCanonicalRecordedLabSpatialFrame,
fetchVegetationShadowResult,
fetchVegetationRouteTgsAnchor,
vegetationFullRouteMaskUrl,
} = await server.ssrLoadModule(
"/src/core/laboratory/vegetationShadow.ts",
));
({ fetchCanonicalRecordedLabSpatialFrame } = await server.ssrLoadModule(
"/src/core/laboratory/canonicalRecordedLabSpatial.ts",
));
});
after(async () => {
@@ -405,16 +407,35 @@ test("canonical recorded LAB spatial frame keeps source, SLAM and body identity
fetcher: async (url) => {
requestedUrl = String(url);
return new Response(JSON.stringify({
schema_version: "missioncore.canonical-recorded-lab-spatial-frame/v1",
schema_version: "missioncore.canonical-recorded-lab-spatial-frame/v2",
target_time_ns: 82_770_000_000,
source_time_ns: 82_769_535_708,
pose_time_ns: 82_769_535_708,
trajectory_time_ns: 82_700_000_000,
coordinate_frame: "body-ground",
sensor_height: {
meters: 0.32,
source: "initial-source-cloud-lower-quantile-median",
sample_count: 20,
mad_m: 0.03,
authority: "visual-derived",
},
spatial_profile: {
profile_id: "source-paced-ground-v2",
local_slam_history_seconds: 5,
local_slam_radius_m: 30,
local_slam_voxel_size_m: 0.12,
local_slam_point_limit: 27000,
},
source_point_count: 2,
source_points_body_xyz_m: [[1, 2, 3], [4, 5, 6]],
local_slam_source_frame_count: 2,
local_slam_source_point_count: 4,
local_slam_point_count: 2,
local_slam_body_xyz_m: [[0, 0, 0], [1, 0, 0]],
body_frame: {
origin_map_xyz_m: [33, 4, 1],
sensor_origin_map_xyz_m: [33, 4, 1.32],
basis_map_from_body: [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
},
}), { status: 200, headers: { "Content-Type": "application/json" } });
@@ -422,10 +443,11 @@ test("canonical recorded LAB spatial frame keeps source, SLAM and body identity
});
assert.equal(
requestedUrl,
`/api/v1/observation-sessions/session-004/canonical-lab/spatial-frame?generation=${generation}&time_ns=82770000000`,
`/api/v1/observation-sessions/session-004/canonical-lab/spatial-frame?generation=${generation}&time_ns=82770000000&profile=source-paced-ground-v2`,
);
assert.equal(frame.sourcePointCount, 2);
assert.equal(frame.localSlamBodyXyzM.length, 2);
assert.equal(frame.sensorHeight.meters, 0.32);
assert.deepEqual(frame.bodyFrame.originMapXyzM, [33, 4, 1]);
});
@@ -461,11 +483,13 @@ test("vegetation realtime LAB and archival benchmark use separate admitted instr
assert.match(resultSource, /RecordedEvidenceVideoScene/);
assert.match(resultSource, /LaboratoryMetricEvidenceScene/);
assert.doesNotMatch(resultSource, /RerunViewport/);
assert.match(resultSource, /fetchCanonicalRecordedLabSpatialFrame/);
assert.match(resultSource, /useCanonicalRecordedLabSpatialFrame/);
assert.doesNotMatch(resultSource, /cacheRef|pumpRef|desiredRef/);
assert.match(resultSource, /useCanonicalRecordedLabReplayState/);
assert.match(resultSource, /playbackTransport="epoch-stream"/);
assert.match(resultSource, /causalTgsCase/);
assert.match(resultSource, /latest causal of 10 sealed anchors/);
assert.match(resultSource, /TGS visible only inside sealed 1 s evidence window/);
assert.match(resultSource, /canonicalRecordedLabPackedTgsCells/);
assert.doesNotMatch(resultSource, /LaboratoryRecordedClipPlayer|M48EvidenceModeRail/);
assert.doesNotMatch(resultSource, /assets\.tgs|<img/);
assert.match(resultSource, /SOURCE POINTS/);