wip(k1): checkpoint connection recovery rewrite
Capture the current unreleased K1 connection, recovery, lifecycle, viewer, and test work as a single known-bad baseline for subsequent fixes.
This commit is contained in:
@@ -142,7 +142,10 @@ def build_l34a_assisted_yolox_error_audit(
|
||||
},
|
||||
"limitations": [
|
||||
"the review was seeded from the same frozen candidate and is not independent truth",
|
||||
"precision, recall and F1 are assisted diagnostic alignment metrics, not acceptance metrics",
|
||||
(
|
||||
"precision, recall and F1 are assisted diagnostic alignment metrics, "
|
||||
"not acceptance metrics"
|
||||
),
|
||||
"custom labels are retained as proposed ontology terms and were not adjudicated",
|
||||
"the result is source-scoped to 32 RAVNOVES00 right-camera frames",
|
||||
],
|
||||
|
||||
@@ -10,7 +10,7 @@ import threading
|
||||
import time
|
||||
import zlib
|
||||
from collections import deque
|
||||
from collections.abc import Mapping, Sequence
|
||||
from collections.abc import Callable, Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from hashlib import sha256
|
||||
from typing import Any, Final, Literal
|
||||
@@ -33,7 +33,7 @@ LiveIngressModality = Literal[
|
||||
|
||||
LIVE_INGRESS_SCHEMA: Final = "missioncore.live-perception-ingress/v1"
|
||||
LIVE_INGRESS_WIRE_SCHEMA: Final = "missioncore.live-perception-wire/v1"
|
||||
LIVE_RESULT_WIRE_SCHEMA: Final = "missioncore.live-perception-result-wire/v1"
|
||||
LIVE_RESULT_WIRE_SCHEMA: Final = "missioncore.live-perception-result-wire/v2"
|
||||
LIVE_RESULT_MAGIC: Final = b"MCPR"
|
||||
LIVE_RESULT_MAX_HEADER_BYTES: Final = 256 * 1024
|
||||
LIVE_RESULT_MAX_PAYLOAD_BYTES: Final = 2 * 1024 * 1024
|
||||
@@ -41,6 +41,8 @@ LIVE_RESULT_MAX_PAYLOAD_BYTES: Final = 2 * 1024 * 1024
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class LivePerceptionResultFrame:
|
||||
session_id: str
|
||||
session_generation: int
|
||||
frame_index: int
|
||||
source_frame_index: int
|
||||
session_seconds: float
|
||||
@@ -53,6 +55,8 @@ class LivePerceptionResultFrame:
|
||||
|
||||
def encode_live_perception_result(
|
||||
*,
|
||||
session_id: str,
|
||||
session_generation: int,
|
||||
frame_index: int,
|
||||
source_frame_index: int,
|
||||
session_seconds: float,
|
||||
@@ -65,7 +69,11 @@ def encode_live_perception_result(
|
||||
"""Encode one bounded, non-authoritative worker-to-viewer result frame."""
|
||||
|
||||
if (
|
||||
frame_index < 0
|
||||
not session_id
|
||||
or len(session_id) > 160
|
||||
or isinstance(session_generation, bool)
|
||||
or session_generation < 1
|
||||
or frame_index < 0
|
||||
or source_frame_index < 0
|
||||
or captured_at_epoch_ns < 0
|
||||
or not math.isfinite(session_seconds)
|
||||
@@ -91,6 +99,8 @@ def encode_live_perception_result(
|
||||
raise ValueError("live perception result payload exceeds the bound")
|
||||
header = {
|
||||
"schema_version": LIVE_RESULT_WIRE_SCHEMA,
|
||||
"session_id": session_id,
|
||||
"session_generation": session_generation,
|
||||
"frame_index": frame_index,
|
||||
"source_frame_index": source_frame_index,
|
||||
"session_seconds": session_seconds,
|
||||
@@ -192,11 +202,19 @@ def decode_live_perception_result(encoded: bytes) -> LivePerceptionResultFrame:
|
||||
mask = np.frombuffer(raw_mask, dtype=np.uint8).reshape((600, 800)).copy()
|
||||
normalized_objects = tuple(_normalize_live_result_object(value) for value in objects)
|
||||
frame_index = header.get("frame_index")
|
||||
session_id = header.get("session_id")
|
||||
session_generation = header.get("session_generation")
|
||||
source_frame_index = header.get("source_frame_index")
|
||||
session_seconds = header.get("session_seconds")
|
||||
captured_at_epoch_ns = header.get("captured_at_epoch_ns")
|
||||
if (
|
||||
not isinstance(frame_index, int)
|
||||
not isinstance(session_id, str)
|
||||
or not session_id
|
||||
or len(session_id) > 160
|
||||
or not isinstance(session_generation, int)
|
||||
or isinstance(session_generation, bool)
|
||||
or session_generation < 1
|
||||
or not isinstance(frame_index, int)
|
||||
or isinstance(frame_index, bool)
|
||||
or frame_index < 0
|
||||
or not isinstance(source_frame_index, int)
|
||||
@@ -212,6 +230,8 @@ def decode_live_perception_result(encoded: bytes) -> LivePerceptionResultFrame:
|
||||
):
|
||||
raise ValueError("live perception result time identity is invalid")
|
||||
return LivePerceptionResultFrame(
|
||||
session_id=session_id,
|
||||
session_generation=session_generation,
|
||||
frame_index=frame_index,
|
||||
source_frame_index=source_frame_index,
|
||||
session_seconds=float(session_seconds),
|
||||
@@ -297,6 +317,7 @@ class LiveIngressEvent:
|
||||
|
||||
ingress_sequence: int
|
||||
session_id: str
|
||||
session_generation: int
|
||||
modality: LiveIngressModality
|
||||
source_id: str
|
||||
source_sequence: int
|
||||
@@ -310,6 +331,7 @@ class LiveIngressEvent:
|
||||
"schema_version": LIVE_INGRESS_WIRE_SCHEMA,
|
||||
"ingress_sequence": self.ingress_sequence,
|
||||
"session_id": self.session_id,
|
||||
"session_generation": self.session_generation,
|
||||
"modality": self.modality,
|
||||
"source_id": self.source_id,
|
||||
"source_sequence": self.source_sequence,
|
||||
@@ -382,9 +404,13 @@ class LivePerceptionIngress:
|
||||
}
|
||||
self._ingress_sequence = 0
|
||||
self._session_id: str | None = None
|
||||
self._session_generation = 0
|
||||
self._active = False
|
||||
self._closed = False
|
||||
self._consumer_id: str | None = None
|
||||
self._results_accepted = 0
|
||||
self._results_rejected_stale = 0
|
||||
self._results_rejected_receiver = 0
|
||||
|
||||
def begin_session(self, session_id: str) -> None:
|
||||
if not session_id or len(session_id) > 160:
|
||||
@@ -402,6 +428,7 @@ class LivePerceptionIngress:
|
||||
for queue in self._queues.values():
|
||||
queue.items.clear()
|
||||
self._session_id = session_id
|
||||
self._session_generation += 1
|
||||
self._active = True
|
||||
self._publish_locked(
|
||||
modality="control",
|
||||
@@ -470,6 +497,31 @@ class LivePerceptionIngress:
|
||||
self._consumer_id = None
|
||||
self._condition.notify_all()
|
||||
|
||||
def admit_result(
|
||||
self,
|
||||
*,
|
||||
session_id: str,
|
||||
session_generation: int,
|
||||
receiver: Callable[[], bool],
|
||||
) -> bool:
|
||||
"""Atomically reject a late worker result before viewer admission."""
|
||||
|
||||
with self._condition:
|
||||
if (
|
||||
self._closed
|
||||
or not self._active
|
||||
or self._session_id != session_id
|
||||
or self._session_generation != session_generation
|
||||
):
|
||||
self._results_rejected_stale += 1
|
||||
return False
|
||||
accepted = receiver()
|
||||
if accepted:
|
||||
self._results_accepted += 1
|
||||
else:
|
||||
self._results_rejected_receiver += 1
|
||||
return accepted
|
||||
|
||||
def take_next(
|
||||
self,
|
||||
consumer_id: str,
|
||||
@@ -509,7 +561,11 @@ class LivePerceptionIngress:
|
||||
"mode": "shadow-diagnostic-only",
|
||||
"active": self._active,
|
||||
"session_id": self._session_id,
|
||||
"session_generation": self._session_generation,
|
||||
"consumer_connected": self._consumer_id is not None,
|
||||
"results_accepted": self._results_accepted,
|
||||
"results_rejected_stale": self._results_rejected_stale,
|
||||
"results_rejected_receiver": self._results_rejected_receiver,
|
||||
"commands_enabled": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
"closed": self._closed,
|
||||
@@ -548,6 +604,7 @@ class LivePerceptionIngress:
|
||||
event = LiveIngressEvent(
|
||||
ingress_sequence=self._ingress_sequence,
|
||||
session_id=session_id,
|
||||
session_generation=self._session_generation,
|
||||
modality=modality,
|
||||
source_id=source_id,
|
||||
source_sequence=source_sequence,
|
||||
|
||||
Reference in New Issue
Block a user