mirror of
https://github.com/azaion/autopilot.git
synced 2026-06-22 16:21:09 +00:00
[AZ-676] [AZ-677] [AZ-678] [AZ-679] telemetry+operator foundation
Batch 15 ships the four foundation tickets sitting on top of AZ-675 (gRPC server) and AZ-667 (mapobjects_store hydrate): * AZ-676: telemetry_stream video path (rtsp_forward + bytes_inline) with ai_locked atomic + session counter, SubscribeVideo RPC. * AZ-677: MapObjects snapshot-on-subscribe + diff broadcast + reconnect-resync (StartThen stream-prepend pattern). * AZ-678: HmacOperatorValidator with per-session monotonic seq, in-process session registry + TTL, constant-time HMAC compare, rejection-reason counters, sliding 60 s sig-failure red-health gate. Trait OperatorCommandValidator in shared::contracts::operator_auth. * AZ-679: PoiSurfaceMapper produces OperatorPoiEvent per architecture §7.10; PoiDequeued events on rotate/age-out/complete; pushed via new TelemetrySink::push_operator_event extension on Topic::OperatorEvent. Cross-task wiring: TelemetrySink trait extended with push_operator_event; OperatorBridge gets optional builder methods with_telemetry_sink / with_validator (composition root wires in AZ-680). Workspace deps: hmac = "0.12"; per-crate adds bytes, serde_json, parking_lot, chrono, uuid, sha2, thiserror. Tests: 14/14 ACs verified locally (4 + 3 + 5 + 3 by AC) plus 6 supporting unit tests + 7 integration tests + 2 shared serde roundtrips. cargo clippy clean on touched crates. Cumulative review for batches 13-15 produced; verdict PASS_WITH_WARNINGS (0 Critical, 0 High, 1 Medium, 4 Low — all carry-overs or deferred-producer notes for AZ-680/AZ-684). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
// AZ-675 telemetry_stream — operator-bound gRPC contract.
|
||||
//
|
||||
// One service, one bi-directional Subscribe RPC. Client opens a stream
|
||||
// declaring which topics it wants; server pushes messages for those
|
||||
// topics until the client disconnects.
|
||||
// One Subscribe RPC multiplexes structured topics (telemetry, gimbal,
|
||||
// detection, movement, MapObjects). Video is carried by a dedicated
|
||||
// SubscribeVideo RPC because frame payloads are binary, large, and
|
||||
// don't share the JSON-broadcast model the structured topics use.
|
||||
//
|
||||
// The server enforces per-client back-pressure: when a client cannot
|
||||
// keep up the oldest message in *that client's* queue is dropped and
|
||||
// a per-(client, topic) drop counter is incremented. Other clients
|
||||
// are unaffected.
|
||||
// The Subscribe server enforces per-client drop-oldest back-pressure
|
||||
// for the structured topics; SubscribeVideo applies the same back-
|
||||
// pressure to the bytes_inline frame queue when the operator client
|
||||
// cannot keep up.
|
||||
//
|
||||
// AZ-676 will add the video path (separate RPC, server-streamed binary
|
||||
// frames). AZ-677 will add the MapObjectsBundle snapshot RPC. Keep
|
||||
// those concerns out of this contract.
|
||||
// MapObjectsBundle (topic on Subscribe) is special: on subscribe the
|
||||
// server first emits a Snapshot variant of MapObjectsBundleMessage
|
||||
// and then forwards Diff variants for in-flight changes. Reconnect
|
||||
// is treated as a new subscribe — a fresh Snapshot is emitted and
|
||||
// diffs accumulated during the disconnect are NOT replayed.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
@@ -26,6 +29,9 @@ enum Topic {
|
||||
TOPIC_DETECTION_EVENT = 3;
|
||||
TOPIC_MOVEMENT_CANDIDATE = 4;
|
||||
TOPIC_MAP_OBJECTS_BUNDLE = 5;
|
||||
// AZ-679 — operator-bound POI events (surfaced + dequeued). JSON
|
||||
// payload is a tagged enum (`kind: poi_surfaced | poi_dequeued`).
|
||||
TOPIC_OPERATOR_EVENT = 6;
|
||||
}
|
||||
|
||||
message SubscribeRequest {
|
||||
@@ -55,10 +61,74 @@ message TelemetryMessage {
|
||||
bytes payload_json = 4;
|
||||
}
|
||||
|
||||
// Pixel format enum mirroring `shared::models::frame::PixelFormat`.
|
||||
// Only used by VideoFrame (bytes_inline mode).
|
||||
enum PixelFormat {
|
||||
PIXEL_FORMAT_UNSPECIFIED = 0;
|
||||
PIXEL_FORMAT_NV12 = 1;
|
||||
PIXEL_FORMAT_YUV420P = 2;
|
||||
PIXEL_FORMAT_RGB24 = 3;
|
||||
}
|
||||
|
||||
// Operator-bound video delivery mode. Per AZ-676 the autopilot is
|
||||
// configured at startup to either forward the RTSP URL straight to
|
||||
// the operator (lower onboard cost; default) or carry encoded bytes
|
||||
// over this gRPC stream.
|
||||
enum VideoMode {
|
||||
VIDEO_MODE_UNSPECIFIED = 0;
|
||||
VIDEO_MODE_RTSP_FORWARD = 1;
|
||||
VIDEO_MODE_BYTES_INLINE = 2;
|
||||
}
|
||||
|
||||
message SubscribeVideoRequest {
|
||||
// Operator/client identifier — plumbed into the ai_locked session
|
||||
// counter, drop counters, and log lines.
|
||||
string client_id = 1;
|
||||
}
|
||||
|
||||
// First message every SubscribeVideo stream emits. Tells the operator
|
||||
// which mode the autopilot is configured in and, for rtsp_forward,
|
||||
// the URL the operator should pull from.
|
||||
message VideoSessionStart {
|
||||
VideoMode mode = 1;
|
||||
// Populated iff `mode == VIDEO_MODE_RTSP_FORWARD`.
|
||||
string rtsp_url = 2;
|
||||
}
|
||||
|
||||
// Encoded video frame (one decoded image from frame_ingest). Emitted
|
||||
// only when `mode == VIDEO_MODE_BYTES_INLINE`.
|
||||
message VideoFrame {
|
||||
uint64 seq = 1;
|
||||
uint64 monotonic_ts_ns = 2;
|
||||
uint32 width = 3;
|
||||
uint32 height = 4;
|
||||
PixelFormat pix_fmt = 5;
|
||||
bytes pixels = 6;
|
||||
}
|
||||
|
||||
// Server-streamed messages on SubscribeVideo. Exactly one start
|
||||
// message is always sent first, followed by zero or more frames
|
||||
// (bytes_inline mode only).
|
||||
message VideoMessage {
|
||||
oneof kind {
|
||||
VideoSessionStart start = 1;
|
||||
VideoFrame frame = 2;
|
||||
}
|
||||
}
|
||||
|
||||
service TelemetryStream {
|
||||
// Server-streaming subscribe. The client sends ONE SubscribeRequest;
|
||||
// the server pushes TelemetryMessage values until the client cancels
|
||||
// the stream or the server shuts down. The server applies per-
|
||||
// client drop-oldest back-pressure if the client cannot keep up.
|
||||
rpc Subscribe(SubscribeRequest) returns (stream TelemetryMessage);
|
||||
|
||||
// AZ-676 operator video path. The first message on every stream is
|
||||
// a VideoSessionStart describing the configured delivery mode; in
|
||||
// rtsp_forward mode no further messages are sent until disconnect.
|
||||
// In bytes_inline mode the server forwards frames published by
|
||||
// frame_ingest with the same per-client drop-oldest back-pressure
|
||||
// as Subscribe (a slow operator loses frames on its own stream
|
||||
// without affecting other clients or the AI pipeline).
|
||||
rpc SubscribeVideo(SubscribeVideoRequest) returns (stream VideoMessage);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user