feat(observatory): add portable calculation profiles
This commit is contained in:
@@ -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"]
|
||||
+401
@@ -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
|
||||
+218
@@ -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"
|
||||
}
|
||||
+51
@@ -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}"
|
||||
Reference in New Issue
Block a user