feat(k1): complete primary acquisition lifecycle
This commit is contained in:
@@ -2,10 +2,18 @@ from __future__ import annotations
|
||||
|
||||
import math
|
||||
import struct
|
||||
from types import SimpleNamespace
|
||||
|
||||
import lz4.block
|
||||
import pytest
|
||||
|
||||
from k1link.device_plugins.xgrids_k1.protocol.modeling import (
|
||||
MODELING_REPORT_TOPIC,
|
||||
ModelingReportDecodeError,
|
||||
decode_modeling_report,
|
||||
is_modeling_report_topic,
|
||||
observe_modeling_report,
|
||||
)
|
||||
from k1link.device_plugins.xgrids_k1.protocol.protobuf_wire import (
|
||||
ProtobufWireError,
|
||||
decode_zigzag64,
|
||||
@@ -21,6 +29,7 @@ from k1link.device_plugins.xgrids_k1.protocol.streams import (
|
||||
decode_lio_pose,
|
||||
decode_pre_path_array,
|
||||
)
|
||||
from k1link.viewer.metrics import BridgeMetrics
|
||||
|
||||
|
||||
def _varint(value: int) -> bytes:
|
||||
@@ -136,6 +145,77 @@ def test_decode_lio_pose_rejects_nonfinite_float() -> None:
|
||||
decode_lio_pose(payload)
|
||||
|
||||
|
||||
def test_decode_modeling_report_device_telemetry() -> None:
|
||||
scan_status = _fixed32(1, 41.521404) + _fixed32(2, 1.375) + _uint(3, 204)
|
||||
payload = _bytes(1, _header()) + _uint(2, 73) + _bytes(3, scan_status)
|
||||
|
||||
telemetry = decode_modeling_report(payload)
|
||||
|
||||
assert telemetry.move_distance_meters == pytest.approx(41.521404)
|
||||
assert telemetry.move_speed_meters_per_second == pytest.approx(1.375)
|
||||
assert telemetry.scan_time_ticks == 204
|
||||
assert telemetry.elapsed_seconds == pytest.approx(102.0)
|
||||
assert telemetry.pgo_progress == 73
|
||||
assert is_modeling_report_topic(MODELING_REPORT_TOPIC)
|
||||
assert not is_modeling_report_topic(f"{MODELING_REPORT_TOPIC}/extra")
|
||||
|
||||
metrics = BridgeMetrics()
|
||||
assert observe_modeling_report(
|
||||
SimpleNamespace(topic=MODELING_REPORT_TOPIC, payload=payload),
|
||||
metrics,
|
||||
)
|
||||
snapshot = metrics.snapshot()
|
||||
assert snapshot["device_elapsed_seconds"] == pytest.approx(102.0)
|
||||
assert snapshot["device_route_distance_meters"] == pytest.approx(41.521)
|
||||
assert snapshot["device_speed_meters_per_second"] == pytest.approx(1.375)
|
||||
assert snapshot["modeling_reports"] == 1
|
||||
assert snapshot["modeling_decode_errors"] == 0
|
||||
|
||||
|
||||
def test_decode_modeling_report_fails_closed_on_invalid_status() -> None:
|
||||
with pytest.raises(ModelingReportDecodeError, match="no scan_status"):
|
||||
decode_modeling_report(_uint(2, 1))
|
||||
with pytest.raises(ModelingReportDecodeError, match="finite and nonnegative"):
|
||||
decode_modeling_report(_bytes(3, _fixed32(1, math.nan)))
|
||||
with pytest.raises(ModelingReportDecodeError, match="configured limit"):
|
||||
decode_modeling_report(b"x" * 5, max_payload_bytes=4)
|
||||
with pytest.raises(ModelingReportDecodeError, match="pgo_progress is duplicated"):
|
||||
decode_modeling_report(_uint(2, 1) + _uint(2, 2) + _bytes(3, _uint(3, 2)))
|
||||
with pytest.raises(ModelingReportDecodeError, match="scan_status is duplicated"):
|
||||
decode_modeling_report(_bytes(3, b"") + _bytes(3, b""))
|
||||
with pytest.raises(ModelingReportDecodeError, match="move_speed is duplicated"):
|
||||
decode_modeling_report(_bytes(3, _fixed32(2, 1.0) + _fixed32(2, 2.0)))
|
||||
with pytest.raises(ModelingReportDecodeError, match="signed range"):
|
||||
decode_modeling_report(_uint(2, 0x8000_0000) + _bytes(3, _uint(3, 2)))
|
||||
|
||||
|
||||
def test_modeling_metrics_follow_device_scan_generation_reset() -> None:
|
||||
metrics = BridgeMetrics()
|
||||
before_reset = _bytes(
|
||||
3,
|
||||
_fixed32(1, 2.071593) + _fixed32(2, 0.2) + _uint(3, 734),
|
||||
)
|
||||
after_reset = _bytes(
|
||||
3,
|
||||
_fixed32(1, 0.0) + _fixed32(2, 0.0) + _uint(3, 2),
|
||||
)
|
||||
|
||||
observe_modeling_report(
|
||||
SimpleNamespace(topic=MODELING_REPORT_TOPIC, payload=before_reset),
|
||||
metrics,
|
||||
)
|
||||
observe_modeling_report(
|
||||
SimpleNamespace(topic=MODELING_REPORT_TOPIC, payload=after_reset),
|
||||
metrics,
|
||||
)
|
||||
|
||||
snapshot = metrics.snapshot()
|
||||
assert snapshot["device_elapsed_seconds"] == 1.0
|
||||
assert snapshot["device_route_distance_meters"] == 0.0
|
||||
assert snapshot["device_speed_meters_per_second"] == 0.0
|
||||
assert snapshot["modeling_reports"] == 2
|
||||
|
||||
|
||||
def test_decode_legacy_pointcloud() -> None:
|
||||
envelope = struct.pack("<III", 16, 123, 456)
|
||||
body = struct.pack("<fffBBBB", 1.0, -2.0, 3.0, 10, 20, 30, 40)
|
||||
|
||||
Reference in New Issue
Block a user