chore(node): preserve pre-canonicalization experiment snapshot

Historical working copy retained for audit before consolidation into main. The canonicalized plugin architecture and later fixes already live in main; this snapshot is not a release or a request to restore obsolete source layout.
This commit is contained in:
DCCONSTRUCTIONS
2026-09-21 08:45:34 +03:00
parent 020a878915
commit 1c7dd29d8a
89 changed files with 5026 additions and 487 deletions
+170
View File
@@ -0,0 +1,170 @@
"""Paired signalling, private ICE candidates and bounded live data channels."""
import asyncio
import ipaddress
import queue
import time
from contextlib import suppress
from uuid import uuid4
import aioice.ice
from aiortc import RTCConfiguration, RTCPeerConnection, RTCSessionDescription
PRIVATE_NETWORKS = tuple(
ipaddress.ip_network(v)
for v in ("127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10")
)
def private(address):
try:
value = ipaddress.ip_address(address)
return value.version == 4 and any(value in network for network in PRIVATE_NETWORKS)
except ValueError:
return False
def admit_sdp(sdp):
if not isinstance(sdp, str) or not 1 <= len(sdp) <= 32768:
raise ValueError("Invalid media invitation")
media = [line for line in sdp.splitlines() if line.startswith("m=")]
if not sdp.startswith("v=0") or len(media) != 1 or not media[0].startswith("m=application "):
raise ValueError("A single data-channel media section is required")
for line in sdp.splitlines():
if line.startswith("a=candidate:"):
parts = line.split()
if (
len(parts) < 8
or parts[7] != "host"
or not (private(parts[4]) or parts[4].endswith(".local"))
):
raise ValueError("Only private host ICE candidates are admitted")
class NodeMediaPeers:
def __init__(self, hub, camera):
self.hub, self.camera, self.items = hub, camera, {}
# Process-local adapter in this dedicated worker; no public/STUN/TURN ICE.
original = aioice.ice.get_host_addresses
aioice.ice.get_host_addresses = lambda use_ipv4, use_ipv6: [
value for value in original(use_ipv4=True, use_ipv6=False) if private(value)
]
async def offer(self, parameters):
admit_sdp(parameters.get("sdp"))
if len(self.items) >= 2:
raise ValueError("Close another live viewer")
pc = RTCPeerConnection(RTCConfiguration(iceServers=[]))
identifier = "peer_" + uuid4().hex
entry = {"pc": pc, "seen": time.monotonic(), "tasks": [], "labels": set()}
self.items[identifier] = entry
@pc.on("datachannel")
def datachannel(channel):
if channel.label not in {"rrd", "camera"} or channel.label in entry["labels"]:
channel.close()
return
entry["labels"].add(channel.label)
@channel.on("message")
def message(value):
if value == "keepalive":
entry["seen"] = time.monotonic()
entry["tasks"].append(asyncio.create_task(self.deliver(identifier, channel)))
@pc.on("connectionstatechange")
async def changed():
if pc.connectionState in {"closed", "failed"}:
await self.close(identifier)
try:
await pc.setRemoteDescription(
RTCSessionDescription(sdp=parameters["sdp"], type="offer")
)
await pc.setLocalDescription(await pc.createAnswer())
async def expiry():
await asyncio.sleep(25)
if pc.connectionState != "connected":
await self.close(identifier)
entry["tasks"].append(asyncio.create_task(expiry()))
snapshot = self.camera.snapshot()
return {
"peer_id": identifier,
"sdp": pc.localDescription.sdp,
"type": "answer",
"camera_mime": (snapshot.get("delivery") or {}).get("media_type"),
"profile": "live-acquisition",
"transport": "webrtc-rrd-fmp4",
}
except BaseException:
await self.close(identifier)
raise
async def send(self, channel, payload):
if len(payload) > 8 * 1024 * 1024:
raise RuntimeError("Preview fragment exceeds bound")
for offset in range(0, len(payload), 16384):
deadline = time.monotonic() + 2
while channel.readyState != "open" or channel.bufferedAmount > 1024 * 1024:
if channel.readyState in {"closed", "closing"} or time.monotonic() > deadline:
raise RuntimeError("Preview consumer unavailable")
await asyncio.sleep(0.01)
channel.send(payload[offset : offset + 16384])
await asyncio.sleep(0)
async def deliver(self, identifier, channel):
subscriber = lease = None
try:
entry = self.items[identifier]
if channel.label == "rrd":
subscriber = await asyncio.to_thread(self.hub.subscribe)
else:
generation = self.camera.snapshot().get("generation")
if generation is None:
channel.close()
return
lease = await asyncio.to_thread(self.camera.open_delivery, generation)
while identifier in self.items and time.monotonic() - entry["seen"] < 30:
if subscriber:
payload = await asyncio.to_thread(subscriber.read)
else:
try:
segment = await asyncio.to_thread(lease.segments.get, 0.5)
except queue.Empty:
continue
if segment is None:
break
kind, payload = segment
if kind == "media":
self.camera.mark_streaming(lease)
if payload is None:
break
if payload:
await self.send(channel, payload)
except (Exception, asyncio.CancelledError):
pass
finally:
if subscriber:
subscriber.close()
if lease:
self.camera.release_delivery(lease, client_closed=True)
if channel.label == "camera":
channel.close()
else:
await self.close(identifier)
async def close(self, identifier):
entry = self.items.pop(identifier, None)
if entry:
for task in entry["tasks"]:
if task is not asyncio.current_task():
task.cancel()
with suppress(Exception):
await entry["pc"].close()
async def close_all(self):
for identifier in list(self.items):
await self.close(identifier)
+144
View File
@@ -0,0 +1,144 @@
"""Bounded live RRD publication for paired Node viewers, without a TCP listener.
Every viewer receives a fresh native recording including StoreInfo/blueprint.
Latest-value queues discard decoded preview frames before encoding; encoded
RRD bytes are never dropped inside a stream. Slow viewers are closed instead.
"""
import queue
import threading
from contextlib import suppress
from uuid import uuid4
from k1link.viewer.rerun_bridge import RerunBridge
MAX_ENCODED_CHUNK = 8 * 1024 * 1024
class RrdSubscriber:
def __init__(self, settings_provider):
self.closed = threading.Event()
self.inputs = queue.Queue(maxsize=2)
self.output = queue.Queue(maxsize=2)
self.settings_provider = settings_provider
self.thread = threading.Thread(target=self.run, name="node-rerun-view", daemon=True)
self.thread.start()
def offer(self, envelope):
if self.closed.is_set():
return
with suppress(queue.Full):
if self.inputs.full():
with suppress(queue.Empty):
self.inputs.get_nowait()
self.inputs.put_nowait(envelope)
def read(self):
if self.closed.is_set():
return None
try:
return self.output.get(timeout=0.5)
except queue.Empty:
return b""
def close(self):
self.closed.set()
def run(self):
binary = None
bridge = None
def output(recording):
nonlocal binary
binary = recording.binary_stream()
return "webrtc+rrd://" + str(uuid4())
try:
bridge = RerunBridge(settings_provider=self.settings_provider, recording_output=output)
bridge.begin_session()
while not self.closed.is_set():
payload = binary.read()
if len(payload) > MAX_ENCODED_CHUNK:
break
if payload:
# Bound both bytes and waiting time. The archive/producer
# never waits for this disposable preview subscription.
self.output.put(payload, timeout=0.5)
try:
envelope = self.inputs.get(timeout=0.1)
except queue.Empty:
continue
bridge.process(envelope)
except Exception:
pass
finally:
self.closed.set()
if bridge is not None:
with suppress(Exception):
bridge.close()
binary.read()
class NodeRerunBridge(RerunBridge):
def __init__(self, **kwargs):
self.lock = threading.Lock()
self.subscribers = []
self.latest = {}
def output(recording):
self.binary = recording.binary_stream()
return "webrtc+rrd://" + str(uuid4())
super().__init__(**kwargs, recording_output=output)
self.binary.read()
def process(self, envelope):
super().process(envelope)
# The primary recording proves native publication and owns metrics.
# It is continuously drained even when no viewer is attached.
self.binary.read()
with self.lock:
self.latest[type(envelope)] = envelope
self.subscribers = [v for v in self.subscribers if not v.closed.is_set()]
for subscriber in self.subscribers:
subscriber.offer(envelope)
def process_perception(self, frame):
super().process_perception(frame)
self.binary.read()
def subscribe(self):
with self.lock:
self.subscribers = [v for v in self.subscribers if not v.closed.is_set()]
if self._closed or len(self.subscribers) >= 2:
raise RuntimeError("Live viewer unavailable")
subscriber = RrdSubscriber(self._settings_provider)
for envelope in self.latest.values():
subscriber.offer(envelope)
self.subscribers.append(subscriber)
return subscriber
def close(self):
with self.lock:
for subscriber in self.subscribers:
subscriber.close()
self.subscribers.clear()
self.latest.clear()
super().close()
self.binary.read()
class NodeRerunHub:
def __init__(self):
self.bridge = None
def create(self, **kwargs):
bridge = NodeRerunBridge(**kwargs)
self.bridge = bridge
return bridge
def subscribe(self):
bridge = self.bridge
if bridge is None:
raise RuntimeError("Live acquisition is not active")
return bridge.subscribe()
+23 -24
View File
@@ -118,6 +118,7 @@ class RerunBridge:
settings_provider: SettingsProvider | None = None,
cors_allow_origin: tuple[str, ...] = DEFAULT_CORS_ORIGINS,
recording_factory: Callable[[str], rr.RecordingStream] | None = None,
recording_output: Callable[[rr.RecordingStream], str] | None = None,
) -> None:
self.metrics = metrics or BridgeMetrics()
self._settings_provider = settings_provider or RerunSceneSettings
@@ -130,31 +131,29 @@ 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=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
# late-client backlog can block the native SDK and freeze preview.
server_memory_limit=LIVE_GRPC_BUFFER_LIMIT,
# Rerun transport can replay ActivateStore before StoreInfo when an
# evicted buffer is served newest-first, leaving late viewers on the
# welcome screen. Preserve protocol order within the bounded cache.
newest_first=False,
cors_allow_origin=list(cors_allow_origin),
)
if recording_output is not None:
# Node's paired WebRTC delivery uses an RRD sink and never opens
# an unauthenticated gRPC listener on an onboard interface.
url = recording_output(recording)
else:
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},
)
url = recording.serve_grpc(
grpc_port=selected_grpc_port,
default_blueprint=blueprint,
# Bounded reconnect cushion, not the source of record.
server_memory_limit=LIVE_GRPC_BUFFER_LIMIT,
# StoreInfo must precede ActivateStore for late viewers.
newest_first=False,
cors_allow_origin=list(cors_allow_origin),
)
recording.send_blueprint(
blueprint,
make_active=True,