Initial commit

This commit is contained in:
2026-04-17 01:33:23 +02:00
commit 1f6543bd7a
28 changed files with 2556 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
[package]
name = "infinity_protocol"
version.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true
[dependencies]
serde.workspace = true

View File

@@ -0,0 +1,206 @@
use serde::{Deserialize, Serialize};
pub const CONTROL_PROTOCOL_VERSION: u16 = 1;
pub const REALTIME_PROTOCOL_VERSION: u16 = 1;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ControlEnvelope {
pub protocol_version: u16,
pub sequence: u32,
pub sent_at_millis: u64,
pub message: ControlMessage,
}
impl ControlEnvelope {
pub fn new(sequence: u32, sent_at_millis: u64, message: ControlMessage) -> Self {
Self {
protocol_version: CONTROL_PROTOCOL_VERSION,
sequence,
sent_at_millis,
message,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RealtimeEnvelope {
pub protocol_version: u16,
pub sequence: u32,
pub sent_at_millis: u64,
pub message: RealtimeMessage,
}
impl RealtimeEnvelope {
pub fn new(sequence: u32, sent_at_millis: u64, message: RealtimeMessage) -> Self {
Self {
protocol_version: REALTIME_PROTOCOL_VERSION,
sequence,
sent_at_millis,
message,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ControlMessage {
DiscoveryHello {
host_id: String,
},
DiscoveryAck {
node_id: String,
firmware_version: String,
capabilities: NodeCapabilities,
},
ConfigSync {
topology_revision: String,
outputs: Vec<PanelAssignment>,
},
HeartbeatRequest {
node_id: String,
},
Heartbeat(NodeHeartbeat),
PresetRecall {
preset_id: String,
transition: TransitionSpec,
},
Blackout {
enabled: bool,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum RealtimeMessage {
SceneParameters(SceneParametersFrame),
PixelFrame(PixelFrame),
ResyncRequest {
node_id: String,
last_sequence_seen: u32,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct NodeCapabilities {
pub outputs_per_node: u8,
pub leds_per_output: u16,
pub supported_driver_backends: Vec<String>,
pub supports_scene_mode: bool,
pub supports_frame_streaming: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct NodeHeartbeat {
pub node_id: String,
pub fps: f32,
pub heartbeat_age_ms: u32,
pub dropped_frames: u32,
pub reconnect_count: u32,
pub free_heap_bytes: Option<u32>,
pub temperature_celsius: Option<f32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PanelAssignment {
pub panel: PanelAddress,
pub physical_output_name: String,
pub driver_reference: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TransitionSpec {
pub transition_ms: u32,
pub mode: TransitionMode,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TransitionMode {
Snap,
Crossfade,
Chase,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SceneParametersFrame {
pub node_id: String,
pub mode: RealtimeMode,
pub preset_id: Option<String>,
pub effect: String,
pub seed: u64,
pub palette: Vec<String>,
pub master_brightness: f32,
pub speed: f32,
pub intensity: f32,
pub target_group: Option<String>,
pub target_outputs: Vec<PanelAddress>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PixelFrame {
pub node_id: String,
pub frame_index: u64,
pub target_outputs: Vec<OutputFrame>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct OutputFrame {
pub panel: PanelAddress,
pub pixels: Vec<Rgb8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Rgb8 {
pub r: u8,
pub g: u8,
pub b: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct PanelAddress {
pub node_id: String,
pub panel_slot: PanelSlot,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum PanelSlot {
Top,
Middle,
Bottom,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RealtimeMode {
DistributedScene,
FrameStreaming,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructors_stamp_current_protocol_versions() {
let control = ControlEnvelope::new(
4,
123,
ControlMessage::HeartbeatRequest {
node_id: "node-01".to_string(),
},
);
let realtime = RealtimeEnvelope::new(
5,
456,
RealtimeMessage::ResyncRequest {
node_id: "node-01".to_string(),
last_sequence_seen: 4,
},
);
assert_eq!(control.protocol_version, CONTROL_PROTOCOL_VERSION);
assert_eq!(realtime.protocol_version, REALTIME_PROTOCOL_VERSION);
}
}