feat(perception): stage TRAVEL qualification
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$BuildContext,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
|
||||
[string]$RunId,
|
||||
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m49-t3-travel"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
function Assert-LastExitCode([string]$Operation) {
|
||||
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
|
||||
}
|
||||
|
||||
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
|
||||
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
|
||||
$null = New-Item -ItemType Directory -Path $Path
|
||||
}
|
||||
$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 Get-Container([string]$Name) {
|
||||
$rows = @((& docker inspect $Name) | ConvertFrom-Json)
|
||||
Assert-LastExitCode "Docker inspection for $Name"
|
||||
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
|
||||
return $rows[0]
|
||||
}
|
||||
|
||||
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
|
||||
throw "M49 T3 is pinned to Worker 006"
|
||||
}
|
||||
|
||||
$context = Resolve-DDirectory $BuildContext "M49 T3 build context" $false
|
||||
$output = Resolve-DDirectory $OutputRoot "M49 T3 output root" $true
|
||||
$runOutput = Join-Path $output $RunId
|
||||
if (Test-Path -LiteralPath $runOutput) { throw "M49 T3 output already exists" }
|
||||
$null = New-Item -ItemType Directory -Path $runOutput
|
||||
$runOutput = Resolve-DDirectory $runOutput "M49 T3 run output" $false
|
||||
$dockerConfig = Join-Path $runOutput "docker-config"
|
||||
$null = New-Item -ItemType Directory -Path $dockerConfig
|
||||
'{"auths":{}}' | Set-Content -LiteralPath (Join-Path $dockerConfig "config.json") -Encoding ascii
|
||||
$env:DOCKER_CONFIG = $dockerConfig
|
||||
|
||||
$os = Get-CimInstance Win32_OperatingSystem
|
||||
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
|
||||
if ($freeMemoryGiB -lt 16.0) {
|
||||
throw ("M49 T3 requires 16 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
|
||||
}
|
||||
|
||||
$canonicalTriton = Get-Container "ndc-mission-core-triton"
|
||||
if (-not $canonicalTriton.State.Running -or $canonicalTriton.State.Health.Status -cne "healthy") {
|
||||
throw "Canonical Mission Core Triton must remain healthy during M49 T3"
|
||||
}
|
||||
|
||||
$containerName = "ndc-mission-core-m49-t3-travel-$RunId"
|
||||
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$containerName$") {
|
||||
throw "M49 T3 container name already exists"
|
||||
}
|
||||
|
||||
$imageTag = "ndc/mission-core-m49-t3-travel:20260826"
|
||||
$buildStarted = [DateTimeOffset]::UtcNow
|
||||
& docker build --pull=false --tag $imageTag $context
|
||||
Assert-LastExitCode "M49 T3 image build"
|
||||
$buildCompleted = [DateTimeOffset]::UtcNow
|
||||
|
||||
$runStarted = [DateTimeOffset]::UtcNow
|
||||
try {
|
||||
& docker run --rm --name $containerName --cpus 16 --memory 24g `
|
||||
--volume ((($runOutput -replace "\\", "/")) + ":/evidence") `
|
||||
$imageTag
|
||||
Assert-LastExitCode "M49 T3 upstream qualification"
|
||||
} finally {
|
||||
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$containerName$") {
|
||||
& docker rm --force $containerName *> $null
|
||||
}
|
||||
}
|
||||
$runCompleted = [DateTimeOffset]::UtcNow
|
||||
|
||||
$image = @((& docker image inspect $imageTag) | ConvertFrom-Json)[0]
|
||||
Assert-LastExitCode "M49 T3 image inspection"
|
||||
$resultPath = Join-Path $runOutput "result.json"
|
||||
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) {
|
||||
throw "M49 T3 result.json is missing"
|
||||
}
|
||||
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
|
||||
if ($result.status -cne "passed") { throw "M49 T3 qualification did not pass" }
|
||||
|
||||
$summary = [ordered]@{
|
||||
schema_version = "missioncore.m49-t3-worker-summary/v1"
|
||||
worker_id = "worker-006"
|
||||
run_id = $RunId
|
||||
image_tag = $imageTag
|
||||
image_id = [string]$image.Id
|
||||
image_size_bytes = [long]$image.Size
|
||||
build_started_utc = $buildStarted.ToString("o")
|
||||
build_wall_seconds = [math]::Round(($buildCompleted - $buildStarted).TotalSeconds, 6)
|
||||
qualification_wall_seconds = [math]::Round(($runCompleted - $runStarted).TotalSeconds, 6)
|
||||
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
|
||||
canonical_triton_id = [string]$canonicalTriton.Id
|
||||
canonical_triton_health = [string]$canonicalTriton.State.Health.Status
|
||||
candidate_accepted = $true
|
||||
ravnoves00_quality_accepted = $false
|
||||
realtime_accepted = $false
|
||||
navigation_or_actuation_allowed = $false
|
||||
}
|
||||
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (
|
||||
Join-Path $runOutput "worker-summary.json"
|
||||
) -Encoding utf8
|
||||
$summary | ConvertTo-Json -Depth 3
|
||||
@@ -0,0 +1,62 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ReleaseRoot,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
|
||||
[string]$RunId
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$taskName = "MissionCore-M49T3Travel"
|
||||
$release = (Resolve-Path -LiteralPath $ReleaseRoot).Path
|
||||
$payload = Join-Path $release "payload"
|
||||
$runner = Join-Path $payload "Invoke-M49T3TravelQualification.ps1"
|
||||
if (-not (Test-Path -LiteralPath $runner -PathType Leaf)) {
|
||||
throw "M49 T3 runner is missing"
|
||||
}
|
||||
|
||||
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
||||
if ($existing -and $existing.State -eq "Running") {
|
||||
throw "$taskName is already running"
|
||||
}
|
||||
|
||||
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
|
||||
$arguments = @(
|
||||
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
|
||||
"-File", "`"$runner`"",
|
||||
"-BuildContext", "`"$payload`"",
|
||||
"-RunId", "`"$RunId`""
|
||||
) -join " "
|
||||
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute $powerShell `
|
||||
-Argument $arguments `
|
||||
-WorkingDirectory $payload
|
||||
$principal = New-ScheduledTaskPrincipal `
|
||||
-UserId $userId `
|
||||
-LogonType Interactive `
|
||||
-RunLevel Limited
|
||||
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-AllowStartIfOnBatteries `
|
||||
-DontStopIfGoingOnBatteries `
|
||||
-StartWhenAvailable `
|
||||
-ExecutionTimeLimit ([TimeSpan]::FromHours(3))
|
||||
|
||||
Register-ScheduledTask `
|
||||
-TaskName $taskName `
|
||||
-Action $action `
|
||||
-Principal $principal `
|
||||
-Trigger $trigger `
|
||||
-Settings $settings `
|
||||
-Description "One-shot M49 T3 pinned TRAVEL upstream qualification." `
|
||||
-Force | Out-Null
|
||||
Start-ScheduledTask -TaskName $taskName
|
||||
|
||||
[pscustomobject]@{
|
||||
task_name = $taskName
|
||||
run_id = $RunId
|
||||
release_root = $release
|
||||
state = (Get-ScheduledTask -TaskName $taskName).State.ToString()
|
||||
} | ConvertTo-Json -Compress
|
||||
@@ -0,0 +1,72 @@
|
||||
FROM ros:jazzy-ros-base@sha256:2589a8fba5257307857890173c069852c2abf913a0be7970f172478baecb09e4
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
ARG TRAVEL_REVISION=95dc2fbd66a343efd9060c45a5711b6307a950a4
|
||||
ARG KITTI_FIXTURE_SHA256=bf272996d5b6d25cc5589e1089137cb20a98b63bd4823a7fea5631b359f6d68c
|
||||
ARG KITTI_GOLD_SHA256=8aaacaa57d17a8a2f043c3ea0a0c134d36a3c50da2385f2364ca4eff53ea9b9f
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
curl \
|
||||
git \
|
||||
libboost-filesystem-dev \
|
||||
libboost-system-dev \
|
||||
libeigen3-dev \
|
||||
libpcl-dev \
|
||||
mpi-default-dev \
|
||||
python3-colcon-common-extensions \
|
||||
ros-jazzy-pcl-conversions \
|
||||
ros-jazzy-rclcpp \
|
||||
ros-jazzy-sensor-msgs \
|
||||
ros-jazzy-std-msgs \
|
||||
time \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /opt/travel/src/TRAVEL \
|
||||
&& git -C /opt/travel/src/TRAVEL init \
|
||||
&& git -C /opt/travel/src/TRAVEL remote add origin https://github.com/url-kaist/TRAVEL.git \
|
||||
&& git -C /opt/travel/src/TRAVEL fetch --depth 1 origin "${TRAVEL_REVISION}" \
|
||||
&& git -C /opt/travel/src/TRAVEL checkout --detach FETCH_HEAD \
|
||||
&& test "$(git -C /opt/travel/src/TRAVEL rev-parse HEAD)" = "${TRAVEL_REVISION}" \
|
||||
&& test -z "$(git -C /opt/travel/src/TRAVEL status --porcelain)"
|
||||
|
||||
RUN mkdir -p /opt/travel/fixture/00/velodyne \
|
||||
&& curl -L --fail --retry 3 \
|
||||
-o /opt/travel/fixture/00/velodyne/000000.bin \
|
||||
https://github.com/url-kaist/TRAVEL/releases/download/test-data-v1/kitti00_000000.bin \
|
||||
&& test "$(stat -c%s /opt/travel/fixture/00/velodyne/000000.bin)" = "1994688" \
|
||||
&& echo "${KITTI_FIXTURE_SHA256} /opt/travel/fixture/00/velodyne/000000.bin" | sha256sum -c - \
|
||||
&& echo "${KITTI_GOLD_SHA256} /opt/travel/src/TRAVEL/cpp/tests/data/kitti00_000000_gold.bin" | sha256sum -c -
|
||||
|
||||
RUN cmake \
|
||||
-S /opt/travel/src/TRAVEL/cpp/travel \
|
||||
-B /opt/travel/core-build \
|
||||
-DTRAVEL_BUILD_EXAMPLES=ON \
|
||||
-DTRAVEL_BUILD_TESTS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
&& cmake --build /opt/travel/core-build \
|
||||
--target regression_kitti run_travel_kitti \
|
||||
--parallel 16
|
||||
|
||||
RUN set +u \
|
||||
&& source /opt/ros/jazzy/setup.bash \
|
||||
&& set -u \
|
||||
&& colcon build \
|
||||
--base-paths /opt/travel/src/TRAVEL \
|
||||
--build-base /opt/travel/ros-build \
|
||||
--install-base /opt/travel/ros-install \
|
||||
--merge-install \
|
||||
--packages-select travel_ros \
|
||||
--cmake-args -DCMAKE_BUILD_TYPE=Release \
|
||||
&& test -x /opt/travel/ros-install/lib/travel_ros/travel_node
|
||||
|
||||
COPY qualify.sh /usr/local/bin/m49-t3-travel-qualify
|
||||
RUN chmod 0755 /usr/local/bin/m49-t3-travel-qualify
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/m49-t3-travel-qualify"]
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
readonly EVIDENCE_ROOT=/evidence
|
||||
readonly RESULT_PATH="${EVIDENCE_ROOT}/result.json"
|
||||
readonly LOG_PATH="${EVIDENCE_ROOT}/qualification.log"
|
||||
readonly RESOURCE_PATH="${EVIDENCE_ROOT}/resource.txt"
|
||||
readonly STARTED_UTC="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
readonly STARTED_NS="$(date +%s%N)"
|
||||
|
||||
mkdir -p "${EVIDENCE_ROOT}"
|
||||
if [[ -e "${RESULT_PATH}" || -e "${LOG_PATH}" || -e "${RESOURCE_PATH}" ]]; then
|
||||
echo "evidence output already exists" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
status=failed
|
||||
failure_stage=bootstrap
|
||||
|
||||
finalize() {
|
||||
local exit_code=$?
|
||||
local completed_ns
|
||||
local elapsed_ms
|
||||
completed_ns="$(date +%s%N)"
|
||||
elapsed_ms="$(( (completed_ns - STARTED_NS) / 1000000 ))"
|
||||
python3 - "${RESULT_PATH}" "${status}" "${failure_stage}" "${exit_code}" \
|
||||
"${STARTED_UTC}" "${elapsed_ms}" <<'PY'
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
target, status, stage, exit_code, started_utc, elapsed_ms = sys.argv[1:]
|
||||
document = {
|
||||
"schema_version": "missioncore.m49-t3-travel-upstream-qualification/v1",
|
||||
"status": status,
|
||||
"failure_stage": None if status == "passed" else stage,
|
||||
"exit_code": int(exit_code),
|
||||
"started_utc": started_utc,
|
||||
"elapsed_ms": int(elapsed_ms),
|
||||
"authority": {
|
||||
"candidate_build_qualified": status == "passed",
|
||||
"ravnoves00_quality_accepted": False,
|
||||
"realtime_accepted": False,
|
||||
"navigation_or_actuation_allowed": False,
|
||||
},
|
||||
"revisions": {
|
||||
"travel": "95dc2fbd66a343efd9060c45a5711b6307a950a4",
|
||||
},
|
||||
"fixture": {
|
||||
"bytes": 1994688,
|
||||
"sha256": "bf272996d5b6d25cc5589e1089137cb20a98b63bd4823a7fea5631b359f6d68c",
|
||||
"gold_sha256": "8aaacaa57d17a8a2f043c3ea0a0c134d36a3c50da2385f2364ca4eff53ea9b9f",
|
||||
},
|
||||
}
|
||||
pathlib.Path(target).write_text(
|
||||
json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8"
|
||||
)
|
||||
PY
|
||||
}
|
||||
trap finalize EXIT
|
||||
|
||||
exec > >(tee "${LOG_PATH}") 2>&1
|
||||
|
||||
failure_stage=source-integrity
|
||||
test "$(git -C /opt/travel/src/TRAVEL rev-parse HEAD)" = \
|
||||
"95dc2fbd66a343efd9060c45a5711b6307a950a4"
|
||||
test -z "$(git -C /opt/travel/src/TRAVEL status --porcelain)"
|
||||
test "$(stat -c%s /opt/travel/fixture/00/velodyne/000000.bin)" = "1994688"
|
||||
echo "bf272996d5b6d25cc5589e1089137cb20a98b63bd4823a7fea5631b359f6d68c /opt/travel/fixture/00/velodyne/000000.bin" | sha256sum -c -
|
||||
echo "8aaacaa57d17a8a2f043c3ea0a0c134d36a3c50da2385f2364ca4eff53ea9b9f /opt/travel/src/TRAVEL/cpp/tests/data/kitti00_000000_gold.bin" | sha256sum -c -
|
||||
|
||||
failure_stage=cpp-kitti-regression
|
||||
/usr/bin/time -v -o "${RESOURCE_PATH}" \
|
||||
/opt/travel/core-build/tests/regression_kitti \
|
||||
/opt/travel/fixture/00/velodyne/000000.bin \
|
||||
/tmp/travel-run-dump.bin \
|
||||
/opt/travel/src/TRAVEL/cpp/tests/data/kitti00_000000_gold.bin
|
||||
|
||||
failure_stage=cpp-example-smoke
|
||||
mkdir -p /tmp/travel-example
|
||||
/opt/travel/core-build/examples/run_travel_kitti \
|
||||
/opt/travel/fixture/00 0 /tmp/travel-example
|
||||
for output in \
|
||||
/tmp/travel-example/0_ground.bin \
|
||||
/tmp/travel-example/0_nonground.bin \
|
||||
/tmp/travel-example/0_labeled.bin; do
|
||||
test -s "${output}"
|
||||
done
|
||||
|
||||
failure_stage=ros2-discovery
|
||||
set +u
|
||||
source /opt/ros/jazzy/setup.bash
|
||||
source /opt/travel/ros-install/setup.bash
|
||||
set -u
|
||||
test -x /opt/travel/ros-install/lib/travel_ros/travel_node
|
||||
test "$(ros2 pkg prefix travel_ros)" = "/opt/travel/ros-install"
|
||||
ros2 pkg executables travel_ros | grep -F "travel_ros travel_node"
|
||||
|
||||
failure_stage=complete
|
||||
status=passed
|
||||
Reference in New Issue
Block a user