feat(observatory): add portable calculation profiles

This commit is contained in:
DCCONSTRUCTIONS
2026-08-31 15:42:56 +03:00
parent d1b75efcea
commit 9beb534108
75 changed files with 24419 additions and 348 deletions
@@ -0,0 +1,37 @@
FROM ndc/mission-core-m49-t3-travel:20260826 AS builder
COPY run_m49_tgs_portable.cpp /build/run_m49_tgs_portable.cpp
RUN echo "52813392aabd02efc5c2b8f7c22ed88e3ef4cc8ad3aafeba2792efe503e29fe9 /build/run_m49_tgs_portable.cpp" \
| sha256sum --check --strict \
&& g++ -std=c++17 -O3 -DNDEBUG -pthread \
-I/opt/travel/src/TRAVEL/cpp/travel/core \
-I/usr/include/eigen3 \
/build/run_m49_tgs_portable.cpp \
-o /build/run_m49_tgs_portable
FROM ndc/mission-core-m49-t3-travel:20260826
COPY --from=builder /build/run_m49_tgs_portable /opt/nodedc/m49-tgs-portable/bin/run_m49_tgs_portable
COPY m49-tgs-portable-v2.json /opt/nodedc/m49-tgs-portable/profile/m49-tgs-portable-v2.json
COPY smoke_m49_tgs_portable.sh /opt/nodedc/m49-tgs-portable/bin/smoke_m49_tgs_portable
RUN echo "6128d6af7e6137f9a9473db045e3b155e2105319159f17c32f344b4aedf823a9 /opt/nodedc/m49-tgs-portable/profile/m49-tgs-portable-v2.json" \
| sha256sum --check --strict \
&& echo "e5da73c2cf89ed0671de8ac52e13014a7faf79ce1ff1f97a090154063a61c18d /opt/nodedc/m49-tgs-portable/bin/smoke_m49_tgs_portable" \
| sha256sum --check --strict \
&& chmod 0555 /opt/nodedc/m49-tgs-portable/bin/smoke_m49_tgs_portable \
&& test -x /opt/nodedc/m49-tgs-portable/bin/run_m49_tgs_portable
LABEL com.nodedc.product="mission-core" \
com.nodedc.stack="ndc-mission-core-observatory" \
com.nodedc.role="m49-tgs-portable-executor" \
com.nodedc.managed-by="mission-core-worker-release" \
com.nodedc.worker-contour="worker-006" \
com.nodedc.authority="observation-only" \
com.nodedc.base-image.sha256="7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f" \
com.nodedc.runner-source.sha256="52813392aabd02efc5c2b8f7c22ed88e3ef4cc8ad3aafeba2792efe503e29fe9" \
com.nodedc.fixture-smoke.sha256="e5da73c2cf89ed0671de8ac52e13014a7faf79ce1ff1f97a090154063a61c18d" \
com.nodedc.profile.sha256="6128d6af7e6137f9a9473db045e3b155e2105319159f17c32f344b4aedf823a9"
ENTRYPOINT ["/opt/nodedc/m49-tgs-portable/bin/run_m49_tgs_portable"]
@@ -0,0 +1,401 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidatePattern("^[a-f0-9]{64}$")]
[string]$CandidateId,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[a-f0-9]{64}$")]
[string]$ArchiveSha256
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$WorkerId = "worker-006"
$ExpectedComputer = "DESKTOP-OPJ8J04"
$ReleaseId = "m49-tgs-portable-executor-v1"
$ReleaseParent = "D:\NDC_MISSIONCORE\runtime\releases\observatory-portable"
$BaseImage = "ndc/mission-core-m49-t3-travel:20260826"
$BaseImageId = "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
$ProfileSha256 = "6128d6af7e6137f9a9473db045e3b155e2105319159f17c32f344b4aedf823a9"
$RunnerSourceSha256 = "52813392aabd02efc5c2b8f7c22ed88e3ef4cc8ad3aafeba2792efe503e29fe9"
$RunnerWrapperSha256 = "2d6c32560682647f868e4ce4c2605749f17c60482a609f8c03ff951411f48ffb"
$FixtureSmokeSha256 = "e5da73c2cf89ed0671de8ac52e13014a7faf79ce1ff1f97a090154063a61c18d"
$ResultContractSha256 = "9dd80c8e2504559d2156fca933de6eb27901e35305e6853aeb84707e1cb13892"
$ProtectedContainers = @(
"ndc-mission-core-triton",
"ndc-mission-core-perception-worker",
"ndc-gaussian-pipeline-gaussian-gateway-1",
"ndc-gaussian-pipeline-gaussian-pipeline-1",
"ndc-gaussian-pipeline-gaussian-terrain-executor-1"
)
$Authority = [ordered]@{
commands_enabled = $false
actuation_allowed = $false
navigation_or_safety_accepted = $false
production_accepted = $false
}
function Assert-LastExitCode([string]$Operation) {
if ($LASTEXITCODE -ne 0) {
throw "$Operation failed with exit code $LASTEXITCODE"
}
}
function Resolve-DDirectory([string]$Path, [string]$Label) {
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
-not $item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: directory"
}
return $item.FullName
}
function Resolve-DFile([string]$Path, [string]$Label) {
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
$item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: file"
}
return $item.FullName
}
function Get-Sha256([string]$Path) {
return (Get-FileHash -Algorithm SHA256 -LiteralPath $Path).Hash.ToLowerInvariant()
}
function Write-Utf8NoBom([string]$Path, [string]$Value) {
$encoding = New-Object System.Text.UTF8Encoding($false)
[IO.File]::WriteAllText($Path, $Value, $encoding)
}
function Get-ProtectedRuntime() {
$rows = @(((& docker inspect $ProtectedContainers) | ConvertFrom-Json))
Assert-LastExitCode "protected runtime inspection"
if ($rows.Count -ne $ProtectedContainers.Count) {
throw "protected runtime inventory is incomplete"
}
return $rows
}
function Assert-ProtectedRuntime([object[]]$Before, [object[]]$After) {
$beforeByName = @{}
foreach ($row in $Before) {
$beforeByName[[string]$row.Name] = [string]$row.Id
}
foreach ($row in $After) {
$name = [string]$row.Name
if (-not $beforeByName.ContainsKey($name) -or $beforeByName[$name] -cne [string]$row.Id) {
throw "protected runtime identity changed: $name"
}
}
$triton = @($After | Where-Object { [string]$_.Name -ceq "/ndc-mission-core-triton" })
if (
$triton.Count -ne 1 -or
-not $triton[0].State.Running -or
[string]$triton[0].State.Health.Status -cne "healthy"
) {
throw "canonical Mission Core Triton is not healthy"
}
}
if ($env:COMPUTERNAME -cne $ExpectedComputer) {
throw "portable M4.9 executor is pinned to Worker 006"
}
$releaseCandidate = Join-Path $ReleaseParent "m49-tgs-portable-candidate-$CandidateId"
$releaseRoot = Resolve-DDirectory $releaseCandidate "portable M4.9 candidate release"
$archive = Resolve-DFile (Join-Path $releaseRoot "candidate.tgz") "portable M4.9 candidate archive"
if ((Get-Sha256 $archive) -cne $ArchiveSha256) {
throw "portable M4.9 candidate archive digest changed"
}
$sourceRoot = Join-Path $releaseRoot "source-candidate"
if (Test-Path -LiteralPath $sourceRoot) {
throw "portable M4.9 source candidate was already extracted"
}
$null = New-Item -ItemType Directory -Path $sourceRoot
& tar -xzf $archive -C $sourceRoot
Assert-LastExitCode "portable M4.9 candidate extraction"
$sourceRoot = Resolve-DDirectory $sourceRoot "portable M4.9 source candidate"
$manifestPath = Resolve-DFile (Join-Path $sourceRoot "release-manifest.json") "portable M4.9 candidate manifest"
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
if (
[string]$manifest.candidate_sha256 -cne $CandidateId -or
[string]$manifest.source_state -cne "uncommitted-candidate" -or
[string]$manifest.state -cne "blocked" -or
[string]$manifest.travel_build_image_sha256 -cne $BaseImageId.Substring(7) -or
[string]$manifest.profile_sha256 -cne $ProfileSha256 -or
[string]$manifest.result_contract_sha256 -cne $ResultContractSha256
) {
throw "portable M4.9 candidate manifest identity changed"
}
$base = @(((& docker image inspect $BaseImage) | ConvertFrom-Json))
Assert-LastExitCode "TRAVEL predecessor image inspection"
if ($base.Count -ne 1 -or [string]$base[0].Id -cne $BaseImageId) {
throw "exact TRAVEL predecessor image changed"
}
$protectedBefore = @(Get-ProtectedRuntime)
Assert-ProtectedRuntime $protectedBefore $protectedBefore
$legacyTaskBefore = Get-ScheduledTask -TaskName "MissionCore-M49TgsFullShadow" -ErrorAction SilentlyContinue
$shortCandidate = $CandidateId.Substring(0, 16)
$imageTag = "ndc/mission-core-m49-tgs-portable-executor:$shortCandidate-candidate"
$existingImageTags = @(
& docker image ls --format "{{.Repository}}:{{.Tag}}" --filter "reference=$imageTag"
)
Assert-LastExitCode "portable M4.9 executor image collision check"
if ($existingImageTags -contains $imageTag) {
throw "portable M4.9 executor image tag already exists"
}
$payload = Resolve-DDirectory (Join-Path $sourceRoot "payload") "portable M4.9 candidate payload"
$dockerfile = Resolve-DFile (
Join-Path $payload "experiments\perception\worker\observatory_portable\Dockerfile.m49-portable-executor"
) "portable M4.9 Dockerfile"
$runnerSource = Resolve-DFile (
Join-Path $payload "experiments\perception\worker\observatory_portable\run_m49_tgs_portable.cpp"
) "portable M4.9 runner source"
$runnerWrapper = Resolve-DFile (
Join-Path $payload "experiments\perception\worker\observatory_portable\run_m49_tgs_portable.sh"
) "portable M4.9 runner wrapper"
$fixtureSmoke = Resolve-DFile (
Join-Path $payload "experiments\perception\worker\observatory_portable\smoke_m49_tgs_portable.sh"
) "portable M4.9 fixture smoke"
$profile = Resolve-DFile (
Join-Path $payload "config\perception\m49-tgs-portable-v2.json"
) "portable M4.9 profile"
if (
(Get-Sha256 $runnerSource) -cne $RunnerSourceSha256 -or
(Get-Sha256 $runnerWrapper) -cne $RunnerWrapperSha256 -or
(Get-Sha256 $fixtureSmoke) -cne $FixtureSmokeSha256 -or
(Get-Sha256 $profile) -cne $ProfileSha256
) {
throw "portable M4.9 exact source files changed"
}
$context = Join-Path $releaseRoot "build-context"
if (Test-Path -LiteralPath $context) {
throw "portable M4.9 immutable build context already exists"
}
$null = New-Item -ItemType Directory -Path $context
Copy-Item -LiteralPath $dockerfile -Destination (Join-Path $context "Dockerfile")
Copy-Item -LiteralPath $runnerSource -Destination (Join-Path $context "run_m49_tgs_portable.cpp")
Copy-Item -LiteralPath $profile -Destination (Join-Path $context "m49-tgs-portable-v2.json")
Copy-Item -LiteralPath $fixtureSmoke -Destination (Join-Path $context "smoke_m49_tgs_portable.sh")
$context = Resolve-DDirectory $context "portable M4.9 immutable build context"
$dockerConfig = Join-Path $releaseRoot "docker-config"
$null = New-Item -ItemType Directory -Path $dockerConfig
Write-Utf8NoBom (Join-Path $dockerConfig "config.json") '{"auths":{}}'
$env:DOCKER_CONFIG = $dockerConfig
$buildOutput = @(& docker build --quiet --pull=false --no-cache --network none --tag $imageTag $context)
Assert-LastExitCode "portable M4.9 executor image build"
$builtImageId = ([string]$buildOutput[-1]).Trim()
$image = @(((& docker image inspect $imageTag) | ConvertFrom-Json))
Assert-LastExitCode "portable M4.9 executor image inspection"
if ($image.Count -ne 1 -or [string]$image[0].Id -cne $builtImageId) {
throw "portable M4.9 executor image identity is ambiguous"
}
$expectedLabels = [ordered]@{
"com.nodedc.product" = "mission-core"
"com.nodedc.stack" = "ndc-mission-core-observatory"
"com.nodedc.role" = "m49-tgs-portable-executor"
"com.nodedc.managed-by" = "mission-core-worker-release"
"com.nodedc.worker-contour" = $WorkerId
"com.nodedc.authority" = "observation-only"
"com.nodedc.base-image.sha256" = $BaseImageId.Substring(7)
"com.nodedc.runner-source.sha256" = $RunnerSourceSha256
"com.nodedc.fixture-smoke.sha256" = $FixtureSmokeSha256
"com.nodedc.profile.sha256" = $ProfileSha256
}
foreach ($entry in $expectedLabels.GetEnumerator()) {
if ([string]$image[0].Config.Labels.($entry.Key) -cne [string]$entry.Value) {
throw "portable M4.9 executor image label changed: $($entry.Key)"
}
}
$installed = Join-Path $releaseRoot "installed"
$null = New-Item -ItemType Directory -Path $installed
$extractName = "ndc-mission-core-m49-portable-extract-$shortCandidate"
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$extractName$") {
throw "portable M4.9 extraction container already exists"
}
try {
$null = & docker create --name $extractName $imageTag
Assert-LastExitCode "portable M4.9 extraction container creation"
& docker cp (
"$extractName`:/opt/nodedc/m49-tgs-portable/bin/run_m49_tgs_portable"
) (Join-Path $installed "run_m49_tgs_portable")
Assert-LastExitCode "portable M4.9 binary extraction"
& docker cp (
"$extractName`:/opt/nodedc/m49-tgs-portable/profile/m49-tgs-portable-v2.json"
) (Join-Path $installed "m49-tgs-portable-v2.json")
Assert-LastExitCode "portable M4.9 profile extraction"
} finally {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$extractName$") {
& docker rm --force $extractName *> $null
}
}
$installedBinary = Resolve-DFile (Join-Path $installed "run_m49_tgs_portable") "installed portable M4.9 runner"
$installedProfile = Resolve-DFile (Join-Path $installed "m49-tgs-portable-v2.json") "installed portable M4.9 profile"
if ((Get-Sha256 $installedProfile) -cne $ProfileSha256) {
throw "installed portable M4.9 profile changed"
}
$binarySha256 = Get-Sha256 $installedBinary
$binaryLength = (Get-Item -LiteralPath $installedBinary).Length
$buildSeal = [ordered]@{
schema_version = "missioncore.m49-tgs-portable-compiled-runner-build-candidate/v1"
source_revision = [string]$manifest.source_revision
source_state = [string]$manifest.source_state
candidate_sha256 = $CandidateId
build_image_sha256 = $BaseImageId.Substring(7)
profile_sha256 = $ProfileSha256
runner_source_sha256 = $RunnerSourceSha256
runner_wrapper_sha256 = $RunnerWrapperSha256
compiler_contract = [ordered]@{
compiler = "g++"
language_standard = "c++17"
flags = @("-O3", "-DNDEBUG", "-pthread")
travel_include = "/opt/travel/src/TRAVEL/cpp/travel/core"
eigen_include = "/usr/include/eigen3"
}
binary = [ordered]@{
file_name = "run_m49_tgs_portable"
format = "elf"
byte_length = [long]$binaryLength
sha256 = $binarySha256
}
authority = $Authority
}
$buildSealPath = Join-Path $installed "compiled-runner-build-candidate.json"
Write-Utf8NoBom $buildSealPath (($buildSeal | ConvertTo-Json -Depth 8 -Compress) + "`n")
$buildSealSha256 = Get-Sha256 $buildSealPath
$smokeName = "ndc-mission-core-m49-portable-smoke-$shortCandidate"
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$smokeName$") {
throw "portable M4.9 smoke container already exists"
}
try {
$smokeOutput = @(& docker run --rm --name $smokeName --network none --read-only --cpus 2 --memory 2g --pids-limit 256 --tmpfs "/smoke:rw,nosuid,nodev,size=32m" --entrypoint /opt/nodedc/m49-tgs-portable/bin/smoke_m49_tgs_portable $imageTag)
Assert-LastExitCode "portable M4.9 fixture smoke"
} finally {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$smokeName$") {
& docker rm --force $smokeName *> $null
}
}
if ($smokeOutput[-1] -cne "M49_PORTABLE_FIXTURE_SMOKE_OK frames=2 points_per_frame=124668") {
throw "portable M4.9 fixture smoke receipt changed"
}
$protectedAfter = @(Get-ProtectedRuntime)
Assert-ProtectedRuntime $protectedBefore $protectedAfter
$legacyTaskAfter = Get-ScheduledTask -TaskName "MissionCore-M49TgsFullShadow" -ErrorAction SilentlyContinue
if (
($null -eq $legacyTaskBefore) -ne ($null -eq $legacyTaskAfter) -or
($null -ne $legacyTaskBefore -and [string]$legacyTaskBefore.State -cne [string]$legacyTaskAfter.State)
) {
throw "legacy M49 scheduled task changed during executor installation"
}
$releaseDocument = [ordered]@{
schema_version = "missioncore.m49-tgs-portable-executor-installed-candidate/v1"
release_id = $ReleaseId
state = "installed-candidate-blocked"
worker_id = $WorkerId
candidate_sha256 = $CandidateId
source_revision = [string]$manifest.source_revision
source_state = [string]$manifest.source_state
source_archive_sha256 = $ArchiveSha256
base_image_sha256 = $BaseImageId.Substring(7)
executor_image = [ordered]@{
tag = $imageTag
sha256 = ([string]$image[0].Id).Substring(7)
size_bytes = [long]$image[0].Size
}
compiled_runner = [ordered]@{
relative_path = "installed/run_m49_tgs_portable"
byte_length = [long]$binaryLength
sha256 = $binarySha256
}
compiled_runner_build_seal = [ordered]@{
relative_path = "installed/compiled-runner-build-candidate.json"
sha256 = $buildSealSha256
}
profile = [ordered]@{
relative_path = "installed/m49-tgs-portable-v2.json"
sha256 = $ProfileSha256
}
result_contract_sha256 = $ResultContractSha256
fixture_smoke = [ordered]@{
source = "pinned TRAVEL KITTI fixture"
frame_count = 2
points_per_frame = 124668
network = "none"
result = "passed"
}
blockers = @("committed-source-snapshot-missing")
authority = $Authority
}
$releasePath = Join-Path $installed "executor-release-candidate.json"
Write-Utf8NoBom $releasePath (($releaseDocument | ConvertTo-Json -Depth 10 -Compress) + "`n")
$releaseSha256 = Get-Sha256 $releasePath
$protectedIdentities = @()
foreach ($row in $protectedAfter) {
$protectedIdentities += [ordered]@{
name = ([string]$row.Name).TrimStart("/")
container_id = [string]$row.Id
}
}
$receipt = [ordered]@{
schema_version = "missioncore.m49-tgs-portable-worker-installation-receipt/v1"
receipt_state = "installed-candidate-blocked"
worker_id = $WorkerId
computer_name = $env:COMPUTERNAME
installed_at_utc = [DateTimeOffset]::UtcNow.ToString("o")
release_id = $ReleaseId
release_sha256 = $releaseSha256
release_root = $releaseRoot
executor_image_sha256 = ([string]$image[0].Id).Substring(7)
compiled_runner_sha256 = $binarySha256
compiled_runner_build_seal_sha256 = $buildSealSha256
fixture_smoke = "passed"
protected_runtime = $protectedIdentities
legacy_m49_task_state = if ($null -eq $legacyTaskAfter) { $null } else { [string]$legacyTaskAfter.State }
blockers = @("committed-source-snapshot-missing")
authority = $Authority
}
$receiptPath = Join-Path $installed "worker-installation-receipt.json"
Write-Utf8NoBom $receiptPath (($receipt | ConvertTo-Json -Depth 10 -Compress) + "`n")
$receiptSha256 = Get-Sha256 $receiptPath
[pscustomobject]@{
ok = $true
receipt_state = "installed-candidate-blocked"
worker_id = $WorkerId
candidate_sha256 = $CandidateId
release_id = $ReleaseId
release_sha256 = $releaseSha256
executor_image_tag = $imageTag
executor_image_sha256 = ([string]$image[0].Id).Substring(7)
compiled_runner_sha256 = $binarySha256
compiled_runner_build_seal_sha256 = $buildSealSha256
worker_installation_receipt_sha256 = $receiptSha256
fixture_smoke = "passed"
blocker = "committed-source-snapshot-missing"
release_root = $releaseRoot
} | ConvertTo-Json -Compress
@@ -0,0 +1,218 @@
{
"assets": [
{
"asset_id": "ddrnet-checkpoint",
"byte_length": 259419077,
"kind": "model-artifact",
"repository_path": null,
"sha256": "b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6"
},
{
"asset_id": "ddrnet-goose-image",
"byte_length": null,
"kind": "container-image",
"repository_path": null,
"sha256": "591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"
},
{
"asset_id": "ddrnet-goose-mapping",
"byte_length": null,
"kind": "runtime-artifact",
"repository_path": null,
"sha256": "88ae319ba5a3877dd3ae0773f693a6a5fdc283934140de9dfaff029108aefd7f"
},
{
"asset_id": "ddrnet-goose-runner",
"byte_length": 32877,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/lab_v1_vegetation_goose/run_goose_vegetation_benchmark.py",
"sha256": "b18ad60f277eea69a240a28f290611b94627fb9707faf1bb3e6e22102dad67c1"
},
{
"asset_id": "ddrnet-image-dockerfile",
"byte_length": 1793,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/lab_v1_vegetation_goose/Dockerfile",
"sha256": "8203fd01e05d8f5bcce11c328dd39dbb6b97df54ca5d9690706536bafe3d3bad"
},
{
"asset_id": "ddrnet-portable-config",
"byte_length": 4324,
"kind": "repository-file",
"repository_path": "config/perception/lab-v1-eomt-ddrnet-portable-v2.json",
"sha256": "c0ff657dadc86607d77d232e84d041fbf2d8b63e86d02319e9cd607220d00f21"
},
{
"asset_id": "eomt-config-json",
"byte_length": 1575,
"kind": "model-artifact",
"repository_path": null,
"sha256": "7f4aa94fa4e43c0dbd79a5420edb511120aef62bd82bfbcbcece79948286a650"
},
{
"asset_id": "eomt-dependency-set",
"byte_length": null,
"kind": "definition-component",
"repository_path": null,
"sha256": "4eb1f8d33236806e74f9e5bb96b7dce2ac37623dc39b2184be2aa8d7d00e983e"
},
{
"asset_id": "eomt-evaluation-helper",
"byte_length": 28915,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/run_evaluation_prelabels.py",
"sha256": "25baf30c0df564734e08f38ace88cc4bc147cacf240c761622279511e361daa4"
},
{
"asset_id": "eomt-image",
"byte_length": null,
"kind": "container-image",
"repository_path": null,
"sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
},
{
"asset_id": "eomt-model-weights",
"byte_length": 1276175488,
"kind": "model-artifact",
"repository_path": null,
"sha256": "c265da9a74f58f5c3f4826d23ca4ca78beac0b106cca5842beca61580de5b782"
},
{
"asset_id": "eomt-orchestrator",
"byte_length": 21489,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/Invoke-E4FullSessionSegmentation.ps1",
"sha256": "d3e9435939444ab35b27a744ac314e289ebd66a13fa56e3d59f121e088d22774"
},
{
"asset_id": "eomt-preprocessor-config",
"byte_length": 666,
"kind": "model-artifact",
"repository_path": null,
"sha256": "97e2fbf7f0bdba2cfc90251c5133bae9c27ddc9c4410509f40670be2332854e7"
},
{
"asset_id": "eomt-profile",
"byte_length": 3805,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/e3_k1_camera1_profile.json",
"sha256": "ea583966bc3409f5cf563cbf4fad05e366907e67187082eb692aff53d9f5d875"
},
{
"asset_id": "eomt-profile-runtime",
"byte_length": 45789,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/run_e3_rectified_segmentation.py",
"sha256": "01881862d4eaa218955f776a948124bf19c34be2b5ec282115daeacb15c53ae6"
},
{
"asset_id": "eomt-recorded-runtime",
"byte_length": 34899,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/run_recorded_perception_epoch.py",
"sha256": "4dcc4fc8bdf33702651a199be69d0dd4fadb243d2e65aee1c3d1ae7a58fdf675"
},
{
"asset_id": "eomt-runner",
"byte_length": 30720,
"kind": "repository-file",
"repository_path": "experiments/perception/worker/run_e4_full_session_segmentation.py",
"sha256": "651e8e06c3912dffb036b7fd08f2c0623f7563d8306cc7aee05db562798518f4"
},
{
"asset_id": "k1-calibration",
"byte_length": null,
"kind": "definition-component",
"repository_path": null,
"sha256": "05f3ad9b38b3a4fc95388a8ec83da83c745e217709e51787b3d5aad0969f6fa9"
},
{
"asset_id": "k1-valid-fov-identity",
"byte_length": null,
"kind": "definition-component",
"repository_path": null,
"sha256": "b4dd8ddf2b87c1d520ee8a0868c4fea062d7c14d1bae73ccabd3abe1f3acbac2"
},
{
"asset_id": "k1-valid-fov-mask",
"byte_length": null,
"kind": "definition-component",
"repository_path": null,
"sha256": "a40cee06b7c6f69b6a09a11563dcfd237f3de833b1ccd31459e66692e528ba63"
},
{
"asset_id": "lab-v1-portable-contracts",
"byte_length": 114905,
"kind": "repository-file",
"repository_path": "src/k1link/observatory/portable_lab_v1_executor.py",
"sha256": "c8ba210336138617a1cdb02ed2795935b25a265ae7b905f75cd0338774338c55"
},
{
"asset_id": "lab-v1-portable-worker",
"byte_length": 46859,
"kind": "repository-file",
"repository_path": "src/k1link/observatory/portable_lab_v1_worker.py",
"sha256": "ad00418d59b793328a281fa432e450919690fe4957f024f296e2d125c1b3c03a"
},
{
"asset_id": "portable-result-contracts",
"byte_length": 22008,
"kind": "repository-file",
"repository_path": "src/k1link/observatory/portable_result_contract.py",
"sha256": "936d6f20789c26e9bed1c9e34ee51259368eb95bc7e3aa549821d1f7de79030c"
},
{
"asset_id": "portable-worker-runtime",
"byte_length": 39120,
"kind": "repository-file",
"repository_path": "src/k1link/observatory/portable_worker_runtime.py",
"sha256": "205d32116f1ba75d5257d15cc79b5146574d93acd52028a5950f6fca4d8129cb"
},
{
"asset_id": "vegetation-policy",
"byte_length": 3022,
"kind": "repository-file",
"repository_path": "config/perception/lab-v1-vegetation-mission-policy-v1.json",
"sha256": "b75c4ac841d7b4bcc57f7a9c8417ca2317d8ecfa499e72a9af8a8591a2ec0d35"
},
{
"asset_id": "vegetation-provider-map",
"byte_length": 2756,
"kind": "repository-file",
"repository_path": "config/perception/lab-v1-vegetation-provider-label-map-v1.json",
"sha256": "f2b69046b6a740fd9532d2d88e7fabae7c20fb662f783c9502adc9026406f352"
}
],
"authority": {
"actuation_allowed": false,
"commands_enabled": false,
"navigation_or_safety_accepted": false,
"production_accepted": false
},
"candidate_sha256": "6e7adbf25acbc3a9daedf8671ee57a7c00043ea73897bc9abf1e9947eaa5d0bd",
"declared_blockers": [
"combined-executor-entrypoint-uninstalled",
"combined-executor-image-unsealed",
"commit-bound-source-unavailable",
"ddrnet-component-port-uninstalled",
"eomt-component-port-uninstalled",
"fixture-smoke-unaccepted",
"worker-installation-receipt-unavailable"
],
"definition_id": "lab-v1-eomt-ddrnet-portable",
"definition_sha256": "3692d41cec3949f348a36eb60a501fb2cd483fed1645679b0ec58061a2fc6dc2",
"definition_version": 2,
"executor_image_sha256": null,
"phases": [
"source-materialization",
"eomt-full-session",
"ddrnet-full-session",
"result-v2-assembly",
"result-v2-validation",
"portable-result-packaging"
],
"release_id": "lab-v1-eomt-ddrnet-worker006-candidate-v2",
"result_contract_sha256": "b3dfaa8e20a0f22fc510d062ac469f010a3281c650059d9ea134f0b3ccb38d9a",
"schema_version": "missioncore.observatory-portable-lab-v1-executor-candidate/v1",
"setup_id": "lab-v1-eomt-ddrnet-portable-v1"
}
@@ -0,0 +1,51 @@
{
"schema_version": "missioncore.m49-tgs-portable-runner-source/v1",
"release_id": "m49-tgs-portable-runner-source-v1",
"worker_contour_id": "worker-006",
"container_image_sha256": "7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f",
"profile_sha256": "6128d6af7e6137f9a9473db045e3b155e2105319159f17c32f344b4aedf823a9",
"input_contract": {
"schema_version": "missioncore.m49-tgs-portable-source-stage/v1",
"sequence": "ordered-kitti-xyzi-float32-files/v1",
"schedule_columns": [
"timeline_frame_index",
"source_frame_index",
"session_seconds",
"available_slot",
"point_count"
],
"timeline_frame_count": "source-derived",
"available_lidar_frame_count": "source-derived",
"missing_lidar_policy": "all-cells-unobserved"
},
"intermediate_result_contract": {
"schema_version": "missioncore.m49-tgs-portable-intermediate/v1",
"outputs": "ground-and-nonground-xyzi-by-timeline-frame",
"timing": "one-row-per-timeline-frame",
"publication_ready": false
},
"files": [
{
"name": "run_m49_tgs_portable.cpp",
"byte_length": 11052,
"sha256": "52813392aabd02efc5c2b8f7c22ed88e3ef4cc8ad3aafeba2792efe503e29fe9"
},
{
"name": "run_m49_tgs_portable.sh",
"byte_length": 858,
"sha256": "2d6c32560682647f868e4ce4c2605749f17c60482a609f8c03ff951411f48ffb"
},
{
"name": "smoke_m49_tgs_portable.sh",
"byte_length": 1117,
"sha256": "e5da73c2cf89ed0671de8ac52e13014a7faf79ce1ff1f97a090154063a61c18d"
}
],
"authority": {
"commands_enabled": false,
"actuation_allowed": false,
"navigation_or_safety_accepted": false,
"production_accepted": false
},
"source_release_sha256": "50818f50892ded83285a0fe2875066e32f2c5a13aa8af6c6ce55f63eef863569"
}
@@ -0,0 +1,266 @@
#include <chrono>
#include <cmath>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "travel/point_types.hpp"
#include "travel/tgs.hpp"
namespace {
using Clock = std::chrono::steady_clock;
struct ScheduleRow {
std::size_t timeline_frame_index;
long long source_frame_index;
double session_seconds;
long long available_slot;
std::size_t point_count;
};
struct Schedule {
std::vector<ScheduleRow> rows;
std::size_t available_count;
};
Schedule readSchedule(const std::string& path) {
std::ifstream input(path);
if (!input) {
throw std::runtime_error("cannot open portable TGS schedule");
}
std::string line;
std::getline(input, line);
if (line !=
"timeline_frame_index\tsource_frame_index\tsession_seconds\tavailable_slot\tpoint_count") {
throw std::runtime_error("portable TGS schedule header changed");
}
std::vector<ScheduleRow> rows;
std::size_t available_count = 0;
long long previous_source_frame = -1;
double previous_session_seconds = -1.0;
while (std::getline(input, line)) {
if (line.empty()) {
throw std::runtime_error("portable TGS schedule contains an empty row");
}
std::istringstream stream(line);
ScheduleRow row{};
std::string trailing;
if (!(stream >> row.timeline_frame_index >> row.source_frame_index >> row.session_seconds >>
row.available_slot >> row.point_count) ||
(stream >> trailing)) {
throw std::runtime_error("portable TGS schedule row is invalid");
}
if (row.timeline_frame_index != rows.size() || row.source_frame_index < 0 ||
row.source_frame_index <= previous_source_frame || !std::isfinite(row.session_seconds) ||
row.session_seconds <= previous_session_seconds) {
throw std::runtime_error("portable TGS schedule order changed");
}
if (row.available_slot == -1) {
if (row.point_count != 0) {
throw std::runtime_error("missing portable TGS frame contains points");
}
} else if (row.available_slot < 0 ||
static_cast<std::size_t>(row.available_slot) != available_count ||
row.point_count == 0) {
throw std::runtime_error("portable TGS available slot order changed");
} else {
++available_count;
}
rows.push_back(row);
previous_source_frame = row.source_frame_index;
previous_session_seconds = row.session_seconds;
}
if (rows.size() < 2 || available_count == 0) {
throw std::runtime_error("portable TGS schedule is incomplete");
}
return Schedule{std::move(rows), available_count};
}
std::string framePath(const std::string& directory, std::size_t slot) {
std::ostringstream name;
name << directory << "/" << std::setw(6) << std::setfill('0') << slot << ".bin";
return name.str();
}
void verifySequence(const std::string& directory, std::size_t expected_count) {
if (!std::filesystem::is_directory(directory)) {
throw std::runtime_error("portable TGS sequence directory is unavailable");
}
std::size_t actual_count = 0;
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
if (!entry.is_regular_file() || entry.path().extension() != ".bin") {
throw std::runtime_error("portable TGS sequence contains an unexpected member");
}
++actual_count;
}
if (actual_count != expected_count) {
throw std::runtime_error("portable TGS sequence and schedule differ");
}
for (std::size_t slot = 0; slot < expected_count; ++slot) {
if (!std::filesystem::is_regular_file(framePath(directory, slot))) {
throw std::runtime_error("portable TGS sequence order changed");
}
}
}
travel::PointCloud<travel::PointXYZI>::Ptr readXYZI(
const std::string& directory,
std::size_t slot,
std::size_t point_count) {
if (point_count > std::numeric_limits<std::size_t>::max() / (4 * sizeof(float))) {
throw std::runtime_error("portable TGS point count exceeds the addressable bound");
}
const auto path = framePath(directory, slot);
const auto expected_bytes = point_count * 4 * sizeof(float);
if (std::filesystem::file_size(path) != expected_bytes) {
throw std::runtime_error("portable TGS input byte count changed");
}
std::ifstream input(path, std::ios::binary);
if (!input) {
throw std::runtime_error("cannot open portable TGS input");
}
std::vector<float> values(point_count * 4);
input.read(
reinterpret_cast<char*>(values.data()),
static_cast<std::streamsize>(expected_bytes));
if (!input || input.peek() != std::ifstream::traits_type::eof()) {
throw std::runtime_error("cannot read exact portable TGS input");
}
auto cloud = std::make_shared<travel::PointCloud<travel::PointXYZI>>();
cloud->resize(point_count);
for (std::size_t index = 0; index < point_count; ++index) {
auto& point = cloud->at(index);
point.x = values[index * 4];
point.y = values[index * 4 + 1];
point.z = values[index * 4 + 2];
point.intensity = values[index * 4 + 3];
if (!std::isfinite(point.x) || !std::isfinite(point.y) ||
!std::isfinite(point.z) || !std::isfinite(point.intensity)) {
throw std::runtime_error("portable TGS input contains a non-finite value");
}
}
return cloud;
}
void writeXYZI(const std::string& path, const travel::PointCloud<PointXYZILID>& cloud) {
std::ofstream output(path, std::ios::binary);
if (!output) {
throw std::runtime_error("cannot open portable TGS output");
}
for (const auto& point : cloud.points) {
const float row[4] = {point.x, point.y, point.z, point.intensity};
output.write(reinterpret_cast<const char*>(row), sizeof(row));
}
if (!output) {
throw std::runtime_error("cannot write portable TGS output");
}
}
double milliseconds(Clock::duration duration) {
return std::chrono::duration<double, std::milli>(duration).count();
}
} // namespace
int main(int argc, char** argv) {
if (argc != 5) {
std::cerr << "Usage: run_m49_tgs_portable <sequence_dir> <schedule.tsv> <output_dir>"
" <timing.tsv>\n";
return 1;
}
try {
const std::string sequence_dir = argv[1];
const std::string schedule_path = argv[2];
const std::string output_dir = argv[3];
const std::string timing_path = argv[4];
const auto schedule = readSchedule(schedule_path);
verifySequence(sequence_dir, schedule.available_count);
if (std::filesystem::exists(output_dir) || std::filesystem::exists(timing_path)) {
throw std::runtime_error("portable TGS output already exists");
}
std::filesystem::create_directories(output_dir);
std::ofstream timing(timing_path);
if (!timing) {
throw std::runtime_error("cannot open portable TGS timing output");
}
timing << "timeline_frame_index\tsource_frame_index\tsession_seconds\tsample_available"
<< "\tavailable_slot\tinput_points\tground_points\tnonground_points\ttgs_ms"
<< "\tstage_wall_ms\n";
timing << std::fixed << std::setprecision(6);
std::size_t completed_slots = 0;
for (const auto& row : schedule.rows) {
const auto stage_started = Clock::now();
std::size_t input_points = 0;
std::size_t ground_points = 0;
std::size_t nonground_points = 0;
double tgs_seconds = 0.0;
if (row.available_slot >= 0) {
const auto slot = static_cast<std::size_t>(row.available_slot);
auto input_xyzi = readXYZI(sequence_dir, slot, row.point_count);
if (input_xyzi->size() != row.point_count) {
throw std::runtime_error("portable TGS input point count changed");
}
auto input = std::make_shared<travel::PointCloud<PointXYZILID>>();
input->reserve(input_xyzi->size());
for (const auto& point : input_xyzi->points) {
PointXYZILID value{};
value.x = point.x;
value.y = point.y;
value.z = point.z;
value.intensity = point.intensity;
value.label = 0;
value.id = 0;
input->emplace_back(value);
}
travel::TravelGroundSeg<PointXYZILID> tgs;
tgs.setParams(
80.0, 1.0, 8.0, 3, 5, 10, 0.5, 0.125, 0.3, 0.940, 200.0, 0.03,
0.1, 1.0, true, false);
travel::PointCloud<PointXYZILID> ground;
travel::PointCloud<PointXYZILID> nonground;
tgs.estimateGround(*input, ground, nonground, tgs_seconds);
input_points = input->size();
ground_points = ground.size();
nonground_points = nonground.size();
const std::string base = output_dir + "/" +
std::to_string(row.timeline_frame_index);
writeXYZI(base + "_ground.bin", ground);
writeXYZI(base + "_nonground.bin", nonground);
++completed_slots;
}
const auto completed = Clock::now();
timing << row.timeline_frame_index << '\t' << row.source_frame_index << '\t'
<< row.session_seconds << '\t' << (row.available_slot >= 0 ? 1 : 0) << '\t'
<< row.available_slot << '\t' << input_points << '\t' << ground_points << '\t'
<< nonground_points << '\t' << (tgs_seconds * 1000.0) << '\t'
<< milliseconds(completed - stage_started) << '\n';
if ((row.timeline_frame_index + 1) % 100 == 0) {
timing.flush();
std::cout << "[TGS-PORTABLE] frame=" << (row.timeline_frame_index + 1) << "/"
<< schedule.rows.size() << " available=" << completed_slots << "/"
<< schedule.available_count << "\n";
}
}
timing.flush();
if (completed_slots != schedule.available_count) {
throw std::runtime_error("portable TGS available frame accounting changed");
}
std::cout << "[TGS-PORTABLE] complete timeline=" << schedule.rows.size()
<< " available=" << completed_slots << "\n";
return 0;
} catch (const std::exception& error) {
std::cerr << "[TGS-PORTABLE] " << error.what() << '\n';
return 2;
}
}
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
readonly PROFILE=/release/m49-tgs-portable-v2.json
readonly PROFILE_SHA256=6128d6af7e6137f9a9473db045e3b155e2105319159f17c32f344b4aedf823a9
readonly SOURCE=/release/run_m49_tgs_portable.cpp
readonly INPUT_ROOT=/work/source/tgs
readonly OUTPUT_ROOT=/work/result
readonly BINARY=/tmp/run_m49_tgs_portable
test -f "${PROFILE}"
test -f "${SOURCE}"
test -f "${INPUT_ROOT}/schedule.tsv"
test -d "${INPUT_ROOT}/sequence"
test ! -e "${OUTPUT_ROOT}"
echo "${PROFILE_SHA256} ${PROFILE}" | sha256sum --check --strict
g++ -std=c++17 -O3 -DNDEBUG -pthread \
-I/opt/travel/src/TRAVEL/cpp/travel/core \
-I/usr/include/eigen3 \
"${SOURCE}" \
-o "${BINARY}"
exec /usr/bin/time -v "${BINARY}" \
"${INPUT_ROOT}/sequence/velodyne" \
"${INPUT_ROOT}/schedule.tsv" \
"${OUTPUT_ROOT}/outputs" \
"${OUTPUT_ROOT}/timing.tsv"
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
readonly SMOKE_ROOT=/smoke
readonly FIXTURE=/opt/travel/fixture/00/velodyne/000000.bin
readonly RUNNER=/opt/nodedc/m49-tgs-portable/bin/run_m49_tgs_portable
readonly EXPECTED_FRAME_BYTES=1994688
readonly EXPECTED_POINTS=124668
mkdir -p "${SMOKE_ROOT}/sequence"
cp "${FIXTURE}" "${SMOKE_ROOT}/sequence/000000.bin"
cp "${FIXTURE}" "${SMOKE_ROOT}/sequence/000001.bin"
printf 'timeline_frame_index\tsource_frame_index\tsession_seconds\tavailable_slot\tpoint_count\n0\t0\t0.0\t0\t124668\n1\t1\t0.1\t1\t124668\n' \
> "${SMOKE_ROOT}/schedule.tsv"
"${RUNNER}" \
"${SMOKE_ROOT}/sequence" \
"${SMOKE_ROOT}/schedule.tsv" \
"${SMOKE_ROOT}/outputs" \
"${SMOKE_ROOT}/timing.tsv"
test "$(wc -l < "${SMOKE_ROOT}/timing.tsv")" -eq 3
for index in 0 1; do
ground_bytes="$(stat -c %s "${SMOKE_ROOT}/outputs/${index}_ground.bin")"
nonground_bytes="$(stat -c %s "${SMOKE_ROOT}/outputs/${index}_nonground.bin")"
test "$((ground_bytes + nonground_bytes))" -eq "${EXPECTED_FRAME_BYTES}"
done
printf 'M49_PORTABLE_FIXTURE_SMOKE_OK frames=2 points_per_frame=%s\n' "${EXPECTED_POINTS}"