Fix onboard K1 enrollment continuity and share named acquisition preparation

This commit is contained in:
DCCONSTRUCTIONS
2026-09-07 18:53:38 +03:00
parent 4f333fc11c
commit eed53dbc0b
23 changed files with 362 additions and 92 deletions
+55 -1
View File
@@ -10,7 +10,11 @@ from missioncore_plugin_sdk.v0alpha2.session import DeviceSessionSnapshot
from k1link.device_plugins.xgrids_k1.linux_host import nm_fields, route_fields
from k1link.device_plugins.xgrids_k1.node_bridge import NodeBridge, plugin_operation_id
from k1link.device_plugins.xgrids_k1.node_sensor import NodeK1Sensor, project_sensor
from k1link.device_plugins.xgrids_k1.node_sensor import (
NodeK1Sensor,
project_sensor,
verification_parameters,
)
from k1link.viewer.node_rerun import NodeRerunHub
@@ -53,6 +57,7 @@ class Facade:
elif request.action_id == "acquisition.prepare":
self.current["acquisition"] = {
"acquisition_id": "acquisition-test",
"project_name": request.parameters["project_name"],
"state": "prepared",
"state_revision": 1,
}
@@ -418,6 +423,8 @@ def test_one_start_intent_preserves_canonical_enter_prepare_start_sequence():
"deadline_at": (datetime.now(UTC) + timedelta(seconds=60)).isoformat(),
"parameters": {
"operator_confirmed": True,
"project_name": " Synthetic survey ",
"mount_type": "handheld", "gnss_mode": "none",
"control_generation": 1,
"acquisition_id": None,
},
@@ -433,6 +440,9 @@ def test_one_start_intent_preserves_canonical_enter_prepare_start_sequence():
journal = OperationJournal()
for action, payload in device.facade.actions:
if action == "acquisition.prepare":
assert payload["project_name"] == "Synthetic survey"
assert payload["mount_type"] == "handheld" and payload["gnss_mode"] == "none"
if action in {"acquisition.prepare", "acquisition.start"}:
row, created = journal.begin(
action, operation_id=payload["operation_id"],
@@ -594,3 +604,47 @@ def test_preview_offer_cannot_cross_acquisition_boundary(replacement):
assert all(action == "state.read" for action, _ in device.facade.actions)
asyncio.run(run())
@pytest.mark.parametrize("draft", [
{}, {"project_name": " "}, {"project_name": "x" * 97},
{"project_name": "invalid\nname"}, {"project_name": "Valid", "mount_type": "uav"},
{"project_name": "Valid", "mount_type": "handheld", "gnss_mode": "rtk"},
])
def test_invalid_start_draft_dispatches_no_workspace_or_project_command(draft):
async def run():
device = bridge()
item = project_sensor(state(), "node-test")
command = {
"operation_id": "op_" + "b" * 32, "action_id": "start",
"session": {"device_id": item["id"], "session_id": "session-test"},
"deadline_at": (datetime.now(UTC) + timedelta(seconds=60)).isoformat(),
"parameters": {"operator_confirmed": True, "control_generation": 1,
"acquisition_id": None, **draft},
}
with pytest.raises(ValueError):
await NodeK1Sensor(device, None).execute(command, "node-test")
assert all(action == "state.read" for action, _ in device.facade.actions)
asyncio.run(run())
def test_recheck_uses_only_exact_admitted_durable_bridge_target():
current = state()
decision = {
"allowed": True, "requires_live_gatt_validation": False,
"required_transport_ref": current["selected_device_id"],
"required_connection_mode": "bridge",
}
current["connection_policy"] = {"actions": {"observe-configured-device-network": decision}}
result = verification_parameters(current, "synthetic-operation")
assert result["source"] == "durable-configured-state"
assert result["device_id"] == current["selected_device_id"]
assert result["expected_mode_revision"] == current["desired_connection_mode_revision"]
for patch in [{"allowed": False}, {"requires_live_gatt_validation": True},
{"required_connection_mode": "quick-connect"},
{"required_transport_ref": "other"}]:
decision.update(patch)
assert "source" not in verification_parameters(current, "synthetic-operation")
decision.update(allowed=True, requires_live_gatt_validation=False,
required_transport_ref=current["selected_device_id"],
required_connection_mode="bridge")