feat: wire physical K1 surface shadow
This commit is contained in:
@@ -84,6 +84,138 @@ def test_e15_profile_rejects_command_authority(tmp_path: Path) -> None:
|
||||
module.read_live_profile(changed)
|
||||
|
||||
|
||||
def test_e28_profile_pins_physical_k1_local_surface_gate() -> None:
|
||||
module = _module()
|
||||
profile_path = (
|
||||
Path(__file__).resolve().parents[1]
|
||||
/ "experiments"
|
||||
/ "perception"
|
||||
/ "worker"
|
||||
/ "e28_physical_k1_local_surface_profile.json"
|
||||
)
|
||||
|
||||
profile, digest = module.read_live_profile(profile_path)
|
||||
|
||||
assert len(digest) == 64
|
||||
assert profile["mode"] == "physical-shadow-gate"
|
||||
assert profile["local_surface"] == {
|
||||
"enabled": True,
|
||||
"profile_id": "k1-vendor-map-dynamic-local-surface/v1",
|
||||
"profile_sha256": (
|
||||
"7a59edc8404d0177a39175578743589bfb6b7822837170cded38ca2b6698cc26"
|
||||
),
|
||||
"point_queue_capacity": 2,
|
||||
"pose_buffer_capacity": 16,
|
||||
"future_pose_wait_ms": 25.0,
|
||||
"retention_seconds": 3.0,
|
||||
"result_capacity": 8,
|
||||
"acceptance": {
|
||||
"minimum_bound_frames": 100,
|
||||
"maximum_pose_miss_fraction": 0.05,
|
||||
"maximum_point_drop_fraction": 0.01,
|
||||
"maximum_runtime_drop_fraction": 0.01,
|
||||
"maximum_p95_result_age_ms": 80.0,
|
||||
},
|
||||
}
|
||||
assert profile["authority"] == {
|
||||
"commands_enabled": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
}
|
||||
|
||||
|
||||
def test_e28_profile_rejects_unpinned_local_surface(tmp_path: Path) -> None:
|
||||
module = _module()
|
||||
source = (
|
||||
Path(__file__).resolve().parents[1]
|
||||
/ "experiments"
|
||||
/ "perception"
|
||||
/ "worker"
|
||||
/ "e28_physical_k1_local_surface_profile.json"
|
||||
)
|
||||
value = json.loads(source.read_text())
|
||||
value["local_surface"]["profile_sha256"] = "0" * 64
|
||||
changed = tmp_path / "unpinned-local-surface.json"
|
||||
changed.write_text(json.dumps(value))
|
||||
|
||||
with pytest.raises(RuntimeError, match="E28 physical local-surface"):
|
||||
module.read_live_profile(changed)
|
||||
|
||||
|
||||
def test_e28_local_surface_acceptance_requires_exact_bounded_accounting() -> None:
|
||||
module = _module()
|
||||
config = {
|
||||
"profile_id": "k1-vendor-map-dynamic-local-surface/v1",
|
||||
"point_queue_capacity": 2,
|
||||
"pose_buffer_capacity": 16,
|
||||
"acceptance": {
|
||||
"minimum_bound_frames": 100,
|
||||
"maximum_pose_miss_fraction": 0.05,
|
||||
"maximum_point_drop_fraction": 0.01,
|
||||
"maximum_runtime_drop_fraction": 0.01,
|
||||
"maximum_p95_result_age_ms": 80.0,
|
||||
},
|
||||
}
|
||||
authority = {
|
||||
"commands_enabled": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
}
|
||||
snapshot = {
|
||||
"closed": True,
|
||||
"authority": authority,
|
||||
"binder": {
|
||||
"points": {
|
||||
"capacity": 2,
|
||||
"depth": 0,
|
||||
"maximum_depth": 2,
|
||||
"published": 102,
|
||||
"bound": 100,
|
||||
"missed": 1,
|
||||
"dropped_overflow": 1,
|
||||
},
|
||||
"poses": {
|
||||
"capacity": 16,
|
||||
"maximum_depth": 12,
|
||||
},
|
||||
},
|
||||
"runtime": {
|
||||
"closed": True,
|
||||
"profile": {
|
||||
"profile_id": "k1-vendor-map-dynamic-local-surface/v1",
|
||||
},
|
||||
"queue": {
|
||||
"capacity": 2,
|
||||
"depth": 0,
|
||||
"maximum_depth": 2,
|
||||
"published": 100,
|
||||
"consumed": 100,
|
||||
"dropped_overflow": 0,
|
||||
},
|
||||
"results": {
|
||||
"published": 100,
|
||||
"failed": 0,
|
||||
"result_age_ms": {"p95": 40.0},
|
||||
},
|
||||
"occupancy_policy": {
|
||||
"absence_of_points_means_free": False,
|
||||
"unknown_is_traversable": False,
|
||||
},
|
||||
"authority": authority,
|
||||
},
|
||||
}
|
||||
|
||||
checks = module._local_surface_acceptance_checks(snapshot, config)
|
||||
|
||||
assert checks
|
||||
assert all(checks.values())
|
||||
snapshot["runtime"]["results"]["result_age_ms"]["p95"] = 90.0
|
||||
assert (
|
||||
module._local_surface_acceptance_checks(snapshot, config)[
|
||||
"local_surface_maximum_p95_result_age_ms"
|
||||
]
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_persistent_worker_request_is_single_run_d_backed_and_token_bounded(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -29,10 +31,33 @@ def test_e15_worker_package_is_minimal_hash_addressed_projection(tmp_path: Path)
|
||||
|
||||
assert package.name == f"e15-worker-package-{manifest['identity_sha256']}"
|
||||
assert manifest["identity"]["classification"] == ("minimal-live-worker-import-projection")
|
||||
assert len(manifest["artifacts"]) == 12
|
||||
assert len(manifest["artifacts"]) == 15
|
||||
assert (package / "k1link" / "compute" / "inline_temporal.py").is_file()
|
||||
assert (
|
||||
package
|
||||
/ "k1link"
|
||||
/ "compute"
|
||||
/ "lidar_local_surface_shadow.py"
|
||||
).is_file()
|
||||
assert not (package / "k1link" / "device_plugins" / "xgrids_k1" / "mqtt").exists()
|
||||
assert (
|
||||
"observation"
|
||||
not in (package / "k1link" / "device_plugins" / "xgrids_k1" / "__init__.py").read_text()
|
||||
)
|
||||
imported = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
(
|
||||
"from k1link.compute.lidar_local_surface_shadow "
|
||||
"import K1LocalSurfaceShadowCoordinator; "
|
||||
"assert K1LocalSurfaceShadowCoordinator"
|
||||
),
|
||||
],
|
||||
cwd=tmp_path,
|
||||
env={**os.environ, "PYTHONPATH": str(package)},
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
assert imported.returncode == 0, imported.stderr
|
||||
|
||||
@@ -19,6 +19,12 @@ from k1link.compute import (
|
||||
K1LocalSurfaceV1,
|
||||
build_k1_local_surface,
|
||||
)
|
||||
from k1link.compute.lidar_local_surface import (
|
||||
DEFAULT_K1_LOCAL_SURFACE_PROFILE as REPLAY_LOCAL_SURFACE_PROFILE,
|
||||
)
|
||||
from k1link.compute.lidar_local_surface_geometry import (
|
||||
DEFAULT_K1_LOCAL_SURFACE_PROFILE as WORKER_LOCAL_SURFACE_PROFILE,
|
||||
)
|
||||
from k1link.web.lidar_api import build_lidar_router
|
||||
|
||||
|
||||
@@ -36,6 +42,10 @@ def _sha256(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def test_replay_and_minimal_worker_pin_the_same_local_surface_profile() -> None:
|
||||
assert WORKER_LOCAL_SURFACE_PROFILE.to_dict() == REPLAY_LOCAL_SURFACE_PROFILE.to_dict()
|
||||
|
||||
|
||||
def _source_pack(root: Path) -> Path:
|
||||
frame_count = 8
|
||||
available = np.asarray([True, True, True, True, True, True, True, False])
|
||||
|
||||
@@ -3,6 +3,12 @@ from __future__ import annotations
|
||||
import threading
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.compute import (
|
||||
K1LocalSurfacePoseBinder,
|
||||
K1LocalSurfaceShadowCoordinator,
|
||||
)
|
||||
from k1link.compute.live_perception import LiveSensorSynchronizer
|
||||
from k1link.data_plane import ConsumerFrameContext, DecodedPointCloudView, DecodedPoseView
|
||||
|
||||
@@ -66,10 +72,7 @@ def test_live_sensor_synchronizer_reports_each_fail_closed_boundary() -> None:
|
||||
assert synchronizer.bind_camera(1_000_000_000).state == "lidar-unavailable"
|
||||
|
||||
synchronizer.publish_point_cloud(_points(1, 900_000_000))
|
||||
assert (
|
||||
synchronizer.bind_camera(1_000_000_000).state
|
||||
== "lidar-camera-delta-exceeded"
|
||||
)
|
||||
assert synchronizer.bind_camera(1_000_000_000).state == "lidar-camera-delta-exceeded"
|
||||
|
||||
synchronizer.publish_point_cloud(_points(2, 1_000_000_000))
|
||||
assert synchronizer.bind_camera(1_000_000_000).state == "pose-unavailable"
|
||||
@@ -116,3 +119,80 @@ def test_live_sensor_synchronizer_storage_never_exceeds_capacity() -> None:
|
||||
assert snapshot["pose_depth"] == 3
|
||||
assert snapshot["evicted_points"] == 4
|
||||
assert snapshot["evicted_poses"] == 4
|
||||
|
||||
|
||||
def test_local_surface_pose_binder_accepts_either_event_order() -> None:
|
||||
points_first = K1LocalSurfacePoseBinder(maximum_pose_binding_ms=100)
|
||||
assert points_first.publish_point_cloud(_points(1, 1_000_000_000)) == ()
|
||||
bound = points_first.publish_pose(_pose(1, 1_010_000_000))
|
||||
assert len(bound) == 1
|
||||
assert bound[0].point_cloud.context.sequence == 1
|
||||
assert bound[0].pose.context.sequence == 1
|
||||
assert bound[0].pose_binding_age_ms == 10.0
|
||||
|
||||
pose_first = K1LocalSurfacePoseBinder(maximum_pose_binding_ms=100)
|
||||
assert pose_first.publish_pose(_pose(1, 2_000_000_000)) == ()
|
||||
bound = pose_first.publish_point_cloud(_points(1, 2_010_000_000))
|
||||
assert len(bound) == 1
|
||||
assert bound[0].pose_binding_age_ms == 10.0
|
||||
|
||||
snapshot = pose_first.snapshot()
|
||||
assert snapshot["points"]["published"] == 1
|
||||
assert snapshot["points"]["bound"] == 1
|
||||
assert snapshot["points"]["missed"] == 0
|
||||
assert snapshot["authority"]["commands_enabled"] is False
|
||||
|
||||
|
||||
def test_local_surface_pose_binder_is_bounded_and_accounts_for_misses() -> None:
|
||||
binder = K1LocalSurfacePoseBinder(
|
||||
maximum_pose_binding_ms=100,
|
||||
point_capacity=2,
|
||||
pose_capacity=3,
|
||||
retention_seconds=10,
|
||||
)
|
||||
for sequence in range(1, 6):
|
||||
assert (
|
||||
binder.publish_point_cloud(_points(sequence, 1_000_000_000 + sequence * 1_000_000))
|
||||
== ()
|
||||
)
|
||||
assert binder.flush() == ()
|
||||
snapshot = binder.snapshot()
|
||||
points = snapshot["points"]
|
||||
assert points["capacity"] == 2
|
||||
assert points["maximum_depth"] == 2
|
||||
assert points["published"] == 5
|
||||
assert points["bound"] == 0
|
||||
assert points["dropped_overflow"] == 3
|
||||
assert points["missed"] == 2
|
||||
assert (
|
||||
points["bound"] + points["dropped_overflow"] + points["missed"] + points["depth"]
|
||||
== points["published"]
|
||||
)
|
||||
|
||||
|
||||
def test_local_surface_pose_binder_fails_closed_after_pose_deadline() -> None:
|
||||
binder = K1LocalSurfacePoseBinder(maximum_pose_binding_ms=100)
|
||||
assert binder.publish_point_cloud(_points(1, 1_000_000_000)) == ()
|
||||
assert binder.publish_pose(_pose(1, 1_200_000_000)) == ()
|
||||
snapshot = binder.snapshot()
|
||||
assert snapshot["points"]["missed"] == 1
|
||||
assert snapshot["points"]["depth"] == 0
|
||||
|
||||
with pytest.raises(ValueError, match="sequence"):
|
||||
binder.publish_pose(_pose(1, 1_210_000_000))
|
||||
|
||||
|
||||
def test_local_surface_shadow_coordinator_closes_exact_accounting() -> None:
|
||||
coordinator = K1LocalSurfaceShadowCoordinator(result_capacity=2)
|
||||
coordinator.begin_session("physical-k1-shadow-test")
|
||||
assert coordinator.publish_pose(_pose(1, 1_000_000_000)) == 0
|
||||
assert coordinator.publish_point_cloud(_points(1, 1_010_000_000)) == 1
|
||||
coordinator.close()
|
||||
|
||||
snapshot = coordinator.snapshot()
|
||||
assert snapshot["closed"] is True
|
||||
assert snapshot["binder"]["points"]["bound"] == 1
|
||||
assert snapshot["runtime"]["queue"]["published"] == 1
|
||||
assert snapshot["runtime"]["queue"]["consumed"] == 1
|
||||
assert snapshot["runtime"]["results"]["failed"] == 0
|
||||
assert snapshot["authority"]["navigation_or_safety_accepted"] is False
|
||||
|
||||
Reference in New Issue
Block a user