Vendor MsQuic with build and install support, add DeviceManager status to configurable heartbeats, and report only enabled devices. Add the local QUIC gateway, protocol coverage, real MsQuic E2E tests, process smoke tests, and updated integration documentation.
231 lines
6.7 KiB
Protocol Buffer
231 lines
6.7 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package cmvr.quic_edge.v1;
|
|
|
|
// Every application message on the edge-opened reliable bidirectional stream
|
|
// is carried by this envelope. message_sequence is strictly increasing per
|
|
// sender (gaps are allowed) and is independent from the heartbeat sequence used
|
|
// for liveness acknowledgement.
|
|
message EdgeControlEnvelope {
|
|
uint32 protocol_version = 1;
|
|
uint64 message_sequence = 2;
|
|
|
|
oneof payload {
|
|
NodeRegisterRequest node_register_request = 10;
|
|
NodeRegisterResponse node_register_response = 11;
|
|
NodeHeartbeat node_heartbeat = 12;
|
|
NodeHeartbeatAck node_heartbeat_ack = 13;
|
|
|
|
MediaSessionOpen media_session_open = 20;
|
|
MediaTrackDescriptor media_track_descriptor = 21;
|
|
MediaSessionClose media_session_close = 22;
|
|
|
|
ProtocolError protocol_error = 30;
|
|
}
|
|
}
|
|
|
|
message NetworkInterfaceAddress {
|
|
enum AddressFamily {
|
|
ADDRESS_FAMILY_UNSPECIFIED = 0;
|
|
ADDRESS_FAMILY_IPV4 = 1;
|
|
ADDRESS_FAMILY_IPV6 = 2;
|
|
}
|
|
|
|
string interface_name = 1;
|
|
string ip_address = 2;
|
|
AddressFamily family = 3;
|
|
bool loopback = 4;
|
|
}
|
|
|
|
// The existing cmvr-es gRPC server remains the robot-control endpoint. The
|
|
// edge advertises its current reachable address through the QUIC control plane.
|
|
message GrpcEndpoint {
|
|
string host = 1;
|
|
uint32 port = 2;
|
|
bool tls = 3;
|
|
}
|
|
|
|
// Stable protocol-level categories for devices managed by cmvr-es. These
|
|
// values intentionally do not reuse the configuration or gRPC API enums:
|
|
// their zero values and supported categories have different semantics.
|
|
enum DeviceKind {
|
|
DEVICE_KIND_UNSPECIFIED = 0;
|
|
DEVICE_KIND_AGV = 1;
|
|
DEVICE_KIND_ARM = 2;
|
|
DEVICE_KIND_BATTERY = 3;
|
|
DEVICE_KIND_BIO_HEAD = 4;
|
|
DEVICE_KIND_CAMERA = 5;
|
|
DEVICE_KIND_CAN_BUS = 6;
|
|
DEVICE_KIND_DEX_HAND = 7;
|
|
DEVICE_KIND_GRIPPER = 8;
|
|
DEVICE_KIND_MICROPHONE = 9;
|
|
DEVICE_KIND_MOTOR = 10;
|
|
DEVICE_KIND_MOTOR_SYSTEM = 11;
|
|
DEVICE_KIND_ROBOT = 12;
|
|
DEVICE_KIND_SPEAKER = 13;
|
|
}
|
|
|
|
// DeviceManager's view of a configured entry. REGISTERED means that the
|
|
// manager owns a device record but has no more specific lifecycle signal.
|
|
enum ManagedDeviceState {
|
|
MANAGED_DEVICE_STATE_UNSPECIFIED = 0;
|
|
MANAGED_DEVICE_STATE_DISABLED = 1;
|
|
MANAGED_DEVICE_STATE_INITIALIZING = 2;
|
|
MANAGED_DEVICE_STATE_REGISTERED = 3;
|
|
MANAGED_DEVICE_STATE_READY = 4;
|
|
MANAGED_DEVICE_STATE_RUNNING = 5;
|
|
MANAGED_DEVICE_STATE_STOPPED = 6;
|
|
MANAGED_DEVICE_STATE_ERROR = 7;
|
|
}
|
|
|
|
// Health is independent of lifecycle. UNSPECIFIED means that no trustworthy
|
|
// health observation is available and must never be interpreted as healthy.
|
|
enum DeviceHealthStatus {
|
|
DEVICE_HEALTH_STATUS_UNSPECIFIED = 0;
|
|
DEVICE_HEALTH_STATUS_HEALTHY = 1;
|
|
DEVICE_HEALTH_STATUS_DEGRADED = 2;
|
|
DEVICE_HEALTH_STATUS_FAULT = 3;
|
|
}
|
|
|
|
message ManagedDeviceStatus {
|
|
string device_id = 1;
|
|
DeviceKind kind = 2;
|
|
|
|
// Concrete implementation name when a device object exists; otherwise a
|
|
// category label. It is for display/diagnostics only. Consumers use kind,
|
|
// rather than this free-form string, for machine decisions.
|
|
string type_name = 3;
|
|
|
|
bool enabled = 4;
|
|
ManagedDeviceState manager_state = 5;
|
|
DeviceHealthStatus health = 6;
|
|
|
|
// false means that no error is currently confirmed. It does not turn
|
|
// DEVICE_HEALTH_STATUS_UNSPECIFIED into a healthy observation.
|
|
bool has_error = 7;
|
|
string error_message = 8;
|
|
|
|
// Time at which DeviceManager last changed the lifecycle/error record.
|
|
// DeviceManagerSnapshot.sampled_at_unix_ms is the freshness timestamp for
|
|
// the health observation carried by this heartbeat.
|
|
uint64 status_updated_at_unix_ms = 9;
|
|
}
|
|
|
|
message DeviceManagerSnapshot {
|
|
string manager_name = 1;
|
|
string manager_version = 2;
|
|
string manager_description = 3;
|
|
|
|
// Current cmvr-es senders include only enabled devices. The enabled field in
|
|
// each row and DISABLED enum value remain part of v1 for wire compatibility.
|
|
repeated ManagedDeviceStatus devices = 4;
|
|
uint64 sampled_at_unix_ms = 5;
|
|
}
|
|
|
|
message NodeDescriptor {
|
|
string node_id = 1;
|
|
string boot_id = 2;
|
|
string software_version = 3;
|
|
repeated NetworkInterfaceAddress local_interfaces = 4;
|
|
GrpcEndpoint grpc_endpoint = 5;
|
|
}
|
|
|
|
// This must be the first application message sent after each QUIC connection
|
|
// is established. A reconnect always creates a new registration session.
|
|
message NodeRegisterRequest {
|
|
NodeDescriptor node = 1;
|
|
uint64 sent_at_unix_ms = 2;
|
|
}
|
|
|
|
message NodeRegisterResponse {
|
|
bool accepted = 1;
|
|
string session_id = 2;
|
|
string message = 3;
|
|
|
|
// Zero tells the edge to keep its locally configured interval.
|
|
uint32 heartbeat_interval_ms = 4;
|
|
|
|
// Derived by the receiver from the authenticated QUIC peer address. It is
|
|
// not copied from a client-supplied local interface.
|
|
string observed_source_ip = 5;
|
|
}
|
|
|
|
// A heartbeat carries a fresh network snapshot so address changes are reported
|
|
// without opening a second protocol or connection.
|
|
message NodeHeartbeat {
|
|
string node_id = 1;
|
|
string boot_id = 2;
|
|
string session_id = 3;
|
|
uint64 sequence = 4;
|
|
uint64 sent_at_unix_ms = 5;
|
|
string software_version = 6;
|
|
repeated NetworkInterfaceAddress local_interfaces = 7;
|
|
GrpcEndpoint grpc_endpoint = 8;
|
|
DeviceManagerSnapshot device_manager = 9;
|
|
}
|
|
|
|
message NodeHeartbeatAck {
|
|
bool accepted = 1;
|
|
uint64 acknowledged_sequence = 2;
|
|
string message = 3;
|
|
uint64 server_time_unix_ms = 4;
|
|
string observed_source_ip = 5;
|
|
string session_id = 6;
|
|
}
|
|
|
|
message MediaSessionOpen {
|
|
string node_id = 1;
|
|
uint64 session_epoch = 2;
|
|
|
|
// Binds the media epoch to the accepted node registration on this QUIC
|
|
// connection. Media must not start before this session is assigned.
|
|
string session_id = 3;
|
|
}
|
|
|
|
enum MediaKind {
|
|
MEDIA_KIND_UNSPECIFIED = 0;
|
|
MEDIA_KIND_VIDEO = 1;
|
|
MEDIA_KIND_AUDIO = 2;
|
|
}
|
|
|
|
message MediaTrackDescriptor {
|
|
uint32 track_id = 1;
|
|
MediaKind kind = 2;
|
|
string device_id = 3;
|
|
string codec = 4;
|
|
uint64 codec_generation = 5;
|
|
|
|
// The exact MediaSourceHub track and the 32-bit token repeated in every
|
|
// DATAGRAM header. The full generation remains on the reliable stream.
|
|
string source_track_id = 6;
|
|
uint32 codec_generation_token = 7;
|
|
string payload_format = 8;
|
|
|
|
// Video fields. They are zero for audio tracks.
|
|
uint32 width = 10;
|
|
uint32 height = 11;
|
|
uint32 frames_per_second = 12;
|
|
|
|
// Audio fields. They are zero for video tracks.
|
|
uint32 sample_rate = 20;
|
|
uint32 channels = 21;
|
|
|
|
// Decoder initialization bytes, for example AVCC/HVCC or AudioSpecificConfig.
|
|
// Existing cmvr-es sources may leave this empty when configuration NAL units
|
|
// are carried in-band.
|
|
bytes codec_config = 30;
|
|
}
|
|
|
|
message MediaSessionClose {
|
|
string reason = 1;
|
|
string session_id = 2;
|
|
uint64 session_epoch = 3;
|
|
}
|
|
|
|
message ProtocolError {
|
|
uint32 code = 1;
|
|
string message = 2;
|
|
uint64 related_message_sequence = 3;
|
|
bool fatal = 4;
|
|
}
|