@@ -1,5 +1,9 @@
import { useEffect , useState } from "react" ;
import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
import { Button , SegmentedControl } from "@nodedc/ui-react" ;
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer" ;
import { LaboratoryRecordedClipPlayer } from "../../components/laboratory/LaboratoryRecordedClipPlayer" ;
import { RerunViewport } from "../../components/RerunViewport" ;
import {
LaboratoryEvidence ,
LaboratoryResultSummary ,
@@ -7,7 +11,18 @@ import {
LaboratoryWorkTemplate ,
} from "../../components/laboratory/LaboratoryPresentation" ;
import {
RecordedEvidenceSemanticMaskOverlay ,
type RecordedEvidenceSemanticClass ,
type RecordedEvidenceSemanticPaletteEntry ,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay" ;
import {
fetchVegetationShadowResult ,
vegetationFullRouteMaskUrl ,
vegetationVideoMaskUrl ,
type VegetationFullRouteLayer ,
type VegetationFullRouteReview ,
type VegetationMixedRouteCase ,
type VegetationMixedRouteReview ,
type VegetationShadowResult ,
} from "../../core/laboratory/vegetationShadow" ;
import {
@@ -15,11 +30,480 @@ 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 {
recordedSessionRerunProfile ,
type RerunPlaybackController ,
} from "../../core/observation/viewerProfile" ;
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts" ;
import { defaultSceneSettings , type SceneSettings } from "../../sceneSettings" ;
import {
M48EvidenceModeRail ,
type M48BlindEvidenceMode ,
} from "./annotation/M48EvidenceModeControls" ;
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 ;
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 nearestTgsCase (
cases : readonly VegetationMixedRouteCase [ ] ,
sequence : number ,
) : VegetationMixedRouteCase | null {
return cases . reduce < VegetationMixedRouteCase | null > ( ( nearest , candidate ) = > (
! nearest
|| Math . abs ( candidate . sourceSequence - sequence )
< Math . abs ( nearest . sourceSequence - sequence )
? candidate
: nearest
) , null ) ;
}
function FullRouteReviewEvidence ( {
resultId ,
review ,
} : {
resultId : string ;
review : VegetationFullRouteReview ;
} ) {
const [ sequence , setSequence ] = useState ( 1 ) ;
const [ playing , setPlaying ] = useState ( false ) ;
const [ playbackRate , setPlaybackRate ] = useState ( 1 ) ;
const [ semanticLayer , setSemanticLayer ] = useState < "city" | "vegetation" > ( "vegetation" ) ;
const [ showCameraSemantic , setShowCameraSemantic ] = useState ( true ) ;
const [ expanded , setExpanded ] = useState ( false ) ;
const [ evidenceMode , setEvidenceMode ] = useState < M48BlindEvidenceMode > ( "3d" ) ;
const [ cameraVisible , setCameraVisible ] = useState ( true ) ;
const [ sceneSettings , setSceneSettings ] = useState < SceneSettings > ( ( ) = > ( {
. . . defaultSceneSettings ,
accumulationSeconds : 12 ,
showPoints : true ,
showTrajectory : 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 spatialControllerRef = useRef < RerunPlaybackController | null > ( null ) ;
const frames = useMemo (
( ) = > review . frameSourceTimesNs . map ( ( sourceTimeNs , index ) = > ( {
sequence : index + 1 ,
sourceTimeNs ,
} ) ) ,
[ review . frameSourceTimesNs ] ,
) ;
const layer = review [ semanticLayer ] ;
const semantic = useMemo ( ( ) = > semanticPresentation ( layer ) , [ layer ] ) ;
const maskSequence = sequence - 1 ;
const prefetchSrcs = useMemo ( ( ) = > showCameraSemantic
? Array . from ( { length : 8 } , ( _ , offset ) = > maskSequence + offset + 1 )
. filter ( ( candidate ) = > candidate < review . frameCount )
. map ( ( candidate ) = > vegetationFullRouteMaskUrl ( resultId , semanticLayer , candidate ) )
: [ ] , [ maskSequence , resultId , review . frameCount , semanticLayer , 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 spatialProfile = useMemo ( ( ) = > replayLaunch ? recordedSessionRerunProfile ( {
sourceUrl : replayLaunch.viewerSourceUrl ,
artifact : {
sourceUrl : replayLaunch.sourceUrl ,
viewerSourceUrl : replayLaunch.viewerSourceUrl ,
byteLength : replayLaunch.byteLength ,
sha256 : replayLaunch.sha256 ,
} ,
autoplayWhenReady : false ,
presentationGate : "ready" ,
expectedTimelineStartSeconds : replayLaunch.timelineStartSeconds ,
expectedTimelineEndSeconds : replayLaunch.timelineEndSeconds ,
initialPlaybackStartSeconds : review.timelineStartSeconds ,
view : "spatial" ,
viewResetGeneration : 0 ,
followTrajectory : false ,
perceptionLayers : {
enabled : false ,
detections2d : false ,
segmentation : false ,
cuboids3d : false ,
} ,
perceptionRetryGeneration : 0 ,
lockPerceptionCameraInteraction : false ,
} ) : null , [ replayLaunch , review . timelineStartSeconds ] ) ;
const activeFrame = frames . find ( ( candidate ) = > candidate . sequence === sequence )
? ? frames [ 0 ]
? ? null ;
const activeSourceTimeNsRef = useRef ( activeFrame ? . sourceTimeNs ? ? null ) ;
activeSourceTimeNsRef . current = activeFrame ? . sourceTimeNs ? ? null ;
const handleSpatialControllerChange = useCallback ( ( controller : RerunPlaybackController | null ) = > {
spatialControllerRef . current = controller ;
const sourceTimeNs = activeSourceTimeNsRef . current ;
if ( ! controller || sourceTimeNs === null ) return ;
controller . setPlaying ( false ) ;
controller . seek ( sourceTimeNs ) ;
} , [ ] ) ;
useEffect ( ( ) = > {
const controller = spatialControllerRef . current ;
if ( ! controller || ! activeFrame ) return ;
controller . setPlaying ( false ) ;
controller . seek ( activeFrame . sourceTimeNs ) ;
} , [ activeFrame ] ) ;
const selectedTgsCase = linkedReview
? nearestTgsCase ( linkedReview . cases , sequence )
: null ;
const selectTgsPlan = useCallback ( ( ) = > {
if ( ! linkedReview ) return ;
const item = nearestTgsCase ( linkedReview . cases , sequence ) ;
if ( ! item ) return ;
setPlaying ( false ) ;
setSequence ( item . sourceSequence ) ;
setEvidenceMode ( "plan" ) ;
} , [ linkedReview , sequence ] ) ;
const handleEvidenceModeChange = useCallback ( ( nextMode : M48BlindEvidenceMode ) = > {
if ( nextMode === "plan" ) {
selectTgsPlan ( ) ;
return ;
}
setEvidenceMode ( nextMode ) ;
} , [ selectTgsPlan ] ) ;
const cameraPresentation = evidenceMode === "camera"
? "primary"
: cameraVisible ? "companion" : "hidden" ;
const spatialScene = evidenceMode === "plan" ? (
selectedTgsCase ? (
< div className = "recorded-evidence-image-scene" >
< img
src = { selectedTgsCase . assets . tgs }
alt = { ` TGS costmap · sequence ${ selectedTgsCase . sourceSequence } ` }
draggable = { false }
/ >
< div className = "m48-clip-player__pane-label" data-pane = "spatial" >
TGS COSTMAP · Я К О Р Ь { selectedTgsCase . sourceSequence } · { selectedTgsCase . tgs . occupiedCells } OCCUPIED
< / div >
< / div >
) : (
< div className = "m4-replay-threat-visual__pane-status" role = "status" >
{ linkedReviewError ? ? "Открываем sealed TGS anchors…" }
< / div >
)
) : spatialProfile ? (
< RerunViewport
profile = { spatialProfile }
sceneSettings = { sceneSettings }
onPlaybackControllerChange = { handleSpatialControllerChange }
/ >
) : (
< div className = "m4-replay-threat-visual__pane-status" role = "status" >
О т к р ы в а е м sealed RRD , source points и SLAM trajectory …
< / div >
) ;
return (
< LaboratoryEvidenceViewer
label = "RAVNOVES004TREE full recorded review"
className = "m48-atlas-visual"
mode = { semanticLayer }
modes = { FULL_ROUTE_SEMANTIC_MODES }
expanded = { expanded }
onModeChange = { setSemanticLayer }
onExpandedChange = { setExpanded }
modeControlsVisible = { false }
chromeLayout = "stacked"
>
< div className = "m48-evidence-stage" >
{ videoSource ? (
< LaboratoryRecordedClipPlayer
source = { videoSource }
segmentCount = { review . frameCount }
frames = { frames }
sequence = { sequence }
playing = { playing }
playbackRate = { playbackRate }
cameraPresentation = { cameraPresentation }
continuousPlayback
sourceCount = { 4 }
onSequenceChange = { setSequence }
onPlayingChange = { setPlaying }
onPlaybackRateChange = { setPlaybackRate }
alternativeScene = { spatialScene }
cameraControls = { (
< >
< 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 ) ;
} }
/ >
< / >
) }
spatialControls = { (
< >
< Button
size = "compact"
shape = "pill"
variant = { sceneSettings . showPoints ? "primary" : "secondary" }
aria-pressed = { sceneSettings . showPoints }
onClick = { ( ) = > {
setEvidenceMode ( "3d" ) ;
setSceneSettings ( ( settings ) = > ( {
. . . settings ,
showPoints : ! settings . showPoints ,
} ) ) ;
} }
>
SOURCE POINTS
< / Button >
< Button
size = "compact"
shape = "pill"
variant = { sceneSettings . showTrajectory ? "primary" : "secondary" }
aria-pressed = { sceneSettings . showTrajectory }
onClick = { ( ) = > {
setEvidenceMode ( "3d" ) ;
setSceneSettings ( ( settings ) = > ( {
. . . settings ,
showTrajectory : ! settings . showTrajectory ,
} ) ) ;
} }
>
LOCAL SLAM
< / Button >
< Button
size = "compact"
shape = "pill"
variant = { evidenceMode === "plan" ? "primary" : "secondary" }
aria-pressed = { evidenceMode === "plan" }
disabled = { ! linkedReview }
title = { linkedReviewError ? ? "10 sealed causal TGS anchors; continuous TGS отсутствует" }
onClick = { selectTgsPlan }
>
TGS COSTMAP
< / Button >
< Button
size = "compact"
shape = "pill"
variant = "secondary"
disabled
title = "Для RAV004 не опубликован point-aligned 3D semantic archive"
>
SEMANTICS
< / Button >
< / >
) }
cameraOverlay = { (
< >
< div className = "m48-clip-player__pane-label" data-pane = "camera" >
{ showCameraSemantic
? ` ${ semanticLayer === "city" ? "EoMT CITY" : "DDRNet NATURE" } · КАДР ${ sequence } / ${ review . frameCount } `
: ` SOURCE · КАДР ${ sequence } / ${ review . frameCount } ` }
< / div >
{ showCameraSemantic ? (
< div className = "m48-clip-player__overlay" >
< RecordedEvidenceSemanticMaskOverlay
src = { vegetationFullRouteMaskUrl ( resultId , semanticLayer , maskSequence ) }
prefetchSrcs = { prefetchSrcs }
imageWidth = { review . width }
imageHeight = { review . height }
classes = { semantic . classes }
palette = { semantic . palette }
opacity = { 0.76 }
ariaLabel = { ` ${ layer . name } semantic prediction ` }
/ >
< / div >
) : null }
< / >
) }
/ >
) : (
< div className = "m4-replay-threat-visual__pane-status" role = { videoError ? "alert" : "status" } >
{ videoError ? ? "Открываем автономный RAVNOVES004TREE source…" }
< / div >
) }
{ videoSource ? (
< M48EvidenceModeRail
mode = { evidenceMode }
cameraVisible = { cameraVisible }
spatialAvailable = { Boolean ( spatialProfile ) }
planAvailable = { Boolean ( linkedReview ) }
onModeChange = { handleEvidenceModeChange }
onCameraVisibleChange = { setCameraVisible }
/ >
) : null }
< / div >
< / LaboratoryEvidenceViewer >
) ;
}
function FullRouteReviewResult ( {
rigLabel ,
resultId ,
review ,
} : {
rigLabel : string ;
resultId : string ;
review : VegetationFullRouteReview ;
} ) {
return (
< LaboratoryWorkTemplate
summary = { (
< LaboratorySummary
title = "LAB V1 · RAVNOVES004TREE · полный маршрут"
description = "Общий recorded-LAB шаблон воспроизводит запись с травой и оврагами: RIGHT camera, исходное облако, SLAM trajectory, два независимых semantic-слоя и десять реально просчитанных TGS-якорей."
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 . 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 : "Authority" , value : ` ${ rigLabel } · VISUAL REVIEW ONLY · commands OFF ` } ,
] }
brief = { {
question : "Что реально видно на полном RAV004-прогоне с высокой травой, оврагами и переходом к городу?" ,
approach : "Одна recorded timeline открывается общим LAB viewer. Camera и RRD синхронизированы; EoMT/DDRNet переключаются на камере, source points и SLAM trajectory — в 3D, TGS — только на десяти запечатанных якорях." ,
principalResult : "RAV004 больше не подменяется RAV00: доступна полная исходная запись и её реальные пространственные слои." ,
limitation : "Ручной truth, continuous TGS и point-aligned 3D semantics отсутствуют. Один повреждённый H.264-пакет на позиции 6092 заменён предыдущим декодированным кадром и отражён в proof." ,
} }
method = { {
completeness : "complete" ,
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 : "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 } ,
] ,
} }
/ >
) }
evidence = { (
< LaboratoryEvidence
eyebrow = "CANONICAL RECORDED LAB · RAVNOVES004TREE"
title = "CAMERA + SOURCE POINTS + LOCAL SLAM + TGS COSTMAP + SEMANTICS · 6830/6830"
kind = "recorded-replay"
resizable
>
< FullRouteReviewEvidence resultId = { resultId } review = { review } / >
< / LaboratoryEvidence >
) }
result = { (
< LaboratoryResultSummary
title = "Полный RAV004 visual review восстановлен; управление не авторизовано"
status = "Recorded evidence ready · 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 отсутствуют" } ,
] }
conclusion = { {
proved : "Полный RAV004 открывается в каноническом recorded viewer с camera, source points, SLAM trajectory, 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." ,
} }
/ >
) }
/ >
) ;
}
function VegetationRouteEvidence ( { result } : { result : VegetationShadowResult } ) {
const route = result . routeVideo ! ;
const linkedTgsResultId = route . linkedTgsResultId ;
@@ -92,6 +576,15 @@ export function VegetationShadowResultView({
rigLabel : string ;
result : VegetationShadowResult ;
} ) {
if ( result . routeFullReview ) {
return (
< FullRouteReviewResult
rigLabel = { rigLabel }
resultId = { result . resultId }
review = { result . routeFullReview }
/ >
) ;
}
if ( ! result . routeVideo ? . linkedTgsResultId ) {
throw new Error (
"Vegetation LAB result has no canonical M4 source timeline and linked M4.9 TGS evidence." ,