fix(k1): stabilize repeated acquisition and live viewer recovery

This commit is contained in:
DCCONSTRUCTIONS
2026-07-29 01:55:12 +03:00
parent 37b8930527
commit 2a97cf28c0
28 changed files with 2776 additions and 286 deletions
+46 -2
View File
@@ -1,6 +1,8 @@
from __future__ import annotations
import logging
import math
import socket
import time
from collections.abc import Callable, Mapping
from contextlib import suppress
@@ -31,6 +33,7 @@ TRAJECTORY_MIN_DISTANCE_METERS = 0.02
TRAJECTORY_PUBLISH_INTERVAL_NS = 500_000_000
LIVE_GRPC_BUFFER_LIMIT = "32MiB"
DEFAULT_GRPC_PORT = 9876
GRPC_PORT_SEARCH_SPAN = 128
DEFAULT_CORS_ORIGINS = (
"http://127.0.0.1:5173",
"http://localhost:5173",
@@ -40,6 +43,8 @@ DEFAULT_CORS_ORIGINS = (
"http://localhost:8000",
)
logger = logging.getLogger("k1link.device_plugins.xgrids_k1.viewer_receiver")
@dataclass(frozen=True, slots=True)
class RerunSceneSettings:
@@ -62,6 +67,34 @@ class RerunSceneSettings:
SettingsProvider = Callable[[], RerunSceneSettings]
def _select_available_grpc_port(
preferred_port: int,
*,
search_span: int = GRPC_PORT_SEARCH_SPAN,
) -> int:
"""Select a local Rerun port without reusing a still-served recording."""
if not 1 <= preferred_port <= 65_535:
raise ValueError("Rerun gRPC port must be between 1 and 65535")
if search_span < 1:
raise ValueError("Rerun gRPC port search span must be positive")
last_port = min(preferred_port + search_span, 65_536)
for candidate in range(preferred_port, last_port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
try:
# Rerun binds all local interfaces. Probe the same address class
# so a previous recording retained by a late viewer is detected.
probe.bind(("0.0.0.0", candidate))
except OSError:
continue
return candidate
raise RuntimeError(
"No local Rerun gRPC port is available in "
f"{preferred_port}..{last_port - 1}"
)
class RerunBridge:
"""Publish transport-neutral canonical envelopes to a Rerun recording."""
@@ -85,9 +118,20 @@ class RerunBridge:
else:
recording = recording_factory("nodedc_mission_core_spatial")
try:
selected_grpc_port = _select_available_grpc_port(grpc_port)
if selected_grpc_port != grpc_port:
logger.info(
"Mission Core selected a new Rerun port because an earlier "
"viewer still owns the preferred listener",
extra={
"event_code": "rerun_grpc_port_rotated",
"preferred_port": grpc_port,
"selected_port": selected_grpc_port,
},
)
blueprint = _blueprint(self._settings)
url = recording.serve_grpc(
grpc_port=grpc_port,
grpc_port=selected_grpc_port,
default_blueprint=blueprint,
# This is a reconnect cushion for the live preview, not the source
# of record. Raw MQTT evidence is persisted independently. A large
@@ -132,7 +176,7 @@ class RerunBridge:
return self._url
def begin_session(self, metrics: BridgeMetrics | None = None) -> None:
"""Reset session state while keeping the process-lifetime server alive."""
"""Initialize the one acquisition owned by this recording server."""
if self._closed:
raise RuntimeError("Rerun bridge is already closed")
if metrics is not None: