diff --git a/protobuf_definitions/control.proto b/protobuf_definitions/control.proto index a9c02ef4..7d312654 100644 --- a/protobuf_definitions/control.proto +++ b/protobuf_definitions/control.proto @@ -261,3 +261,13 @@ message SetSotTargetCtrl { // Issue a command to clear the single-object tracking (SOT) target (stop tracking). message ClearSotTargetCtrl { } + +// Request to add an annotation to the current dive. +// +// Sent by the app when a diver picks a type from the in-dive command palette. +// The drone stamps the authoritative time, publishes the result as an +// AnnotationTel to every connected client, and writes it to the dive logfile, so +// all clients converge on the same set. Fire-and-forget, like LightsCtrl. +message AnnotationCtrl { + Annotation annotation = 1; // The annotation to record. The drone owns the timestamp, not this message. +} diff --git a/protobuf_definitions/message_formats.proto b/protobuf_definitions/message_formats.proto index 76ca9869..130fdf11 100644 --- a/protobuf_definitions/message_formats.proto +++ b/protobuf_definitions/message_formats.proto @@ -1621,3 +1621,33 @@ message SotState { uint32 image_width = 3; // Width of the source frame in pixels. uint32 image_height = 4; // Height of the source frame in pixels. } + +// Origin of an annotation. Mirrors the AnnotationSource enum in Blueye Cloud. +enum AnnotationSource { + ANNOTATION_SOURCE_UNSPECIFIED = 0; // Origin not specified. + ANNOTATION_SOURCE_CLOUD = 1; // Created in Blueye Cloud after the dive. + ANNOTATION_SOURCE_APP = 2; // Created in the app during the dive. + ANNOTATION_SOURCE_DRONE = 3; // Emitted by the drone itself. +} + +// A single annotation added during a dive, e.g. by a diver tapping a type in the +// app command palette, or emitted by the drone itself. +// +// Self-contained: it carries the type's display fields (name/icon/color) inline, +// so the drone, any connected client, and the dive logfile can render it without +// a separate catalog. The app keeps the full catalog locally (synced from +// Blueye Cloud) to drive its palette, but only complete annotations go over the +// wire. type_slug lets Blueye Cloud correlate the annotation to its type on import. +// +// The drone owns the timestamp: when this is logged the enclosing BinlogRecord +// carries the time, like any other sample, so there is no time field here. +message Annotation { + string type_slug = 1; // Stable type id for grouping / Cloud correlation. Empty for a free-form annotation. + string type_name = 2; // Type display name, e.g. "Hazard". Snapshot of the type at creation time. + string type_icon = 3; // Font Awesome solid icon name, kebab-case (e.g. "triangle-exclamation"). + string type_color = 4; // Type color, RGB hex incl. leading '#', 7 chars (e.g. "#DC2626"). + string title = 5; // Optional annotation title; Cloud falls back to type_name if empty. Max 200 chars. + string body = 6; // Optional free-form note. Unbounded. + google.protobuf.Duration duration = 7; // Optional span; omit for a point-in-time marker. + AnnotationSource source = 8; // Origin of the annotation; the app sets APP, the drone sets DRONE for its own. +} diff --git a/protobuf_definitions/telemetry.proto b/protobuf_definitions/telemetry.proto index 1ed074e5..117187d3 100644 --- a/protobuf_definitions/telemetry.proto +++ b/protobuf_definitions/telemetry.proto @@ -362,3 +362,14 @@ message CameraPanTiltZoomTel { message SotStateTel { SotState sot_state = 1; // Current SOT state and bounding box. } + +// An annotation that now exists on the dive. +// +// Either added by a client (the drone echoes its AnnotationCtrl here, source APP) +// or emitted by the drone itself (source DRONE); CLOUD is the post-dive Blueye +// Cloud path and is not seen here. Published to every connected client and +// written to the dive logfile. A late-joining client can fetch the most recent +// one with GetTelemetryReq("AnnotationTel"). +message AnnotationTel { + Annotation annotation = 1; // The annotation that was added. +}