syntax = "proto3"; package telemetry; // Top-level wrapper. Every message on the WebSocket is an Envelope. // `oneof` enforces mutual exclusivity at the schema level. message Envelope { oneof payload { DataPacket data = 1; LogPacket log = 2; } } // One sample packet covering all channels and all status fields. // All fields optional: any field absent means "no update for this field // in this packet" — the UI renders missing channel data as a gap and // missing status fields walk back to find the most recent value. message DataPacket { optional int64 timestamp_us = 1; optional double ch1 = 2; optional double ch2 = 3; optional double ch3 = 4; optional double ch4 = 5; optional double ch5 = 6; optional double ch6 = 7; optional double ch7 = 8; optional double ch8 = 9; optional double ch9 = 10; optional double ch10 = 11; optional double ch11 = 12; optional double ch12 = 13; optional double ch13 = 14; optional double ch14 = 15; optional double ch15 = 16; optional double ch16 = 17; // True = pause requested by server. UI button OR-composes with this. optional bool pause = 18; // 4-state status indicators. Defined values: 0=gray, 1=red, 2=yellow, 3=green. // Other values render as gray with a one-time warning logged. optional uint32 status1 = 19; optional uint32 status2 = 20; optional uint32 status3 = 21; optional uint32 status4 = 22; optional uint32 status5 = 23; optional uint32 status6 = 24; optional uint32 status7 = 25; optional uint32 status8 = 26; } // One log entry. Description is taken verbatim and is the single source // of truth for the human-readable text — the app does not maintain a // separate error_number → description mapping. message LogPacket { optional int64 timestamp_us = 1; optional Severity severity = 2; optional uint32 error_number = 3; optional string description = 4; } enum Severity { SEVERITY_UNKNOWN = 0; DEBUG = 1; INFO = 2; WARN = 3; ERROR = 4; FATAL = 5; }