Skip to main content

rill_core/queues/
telemetry.rs

1//! Очередь телеметрии — данные обратной связи из звукового мира
2
3use super::command::Command;
4use crate::traits::{NodeId, ParameterId, PortId};
5use std::fmt;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8/// Тип телеметрии (для идентификации)
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum TelemetryKind {
11    /// Значение параметра
12    Parameter,
13    /// Аудио данные
14    Audio,
15    /// Пиковое значение
16    Peak,
17    /// Событие
18    Event,
19    /// Нарушение микро-контроля
20    Violation,
21}
22
23impl fmt::Display for TelemetryKind {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            TelemetryKind::Parameter => write!(f, "parameter"),
27            TelemetryKind::Audio => write!(f, "audio"),
28            TelemetryKind::Peak => write!(f, "peak"),
29            TelemetryKind::Event => write!(f, "event"),
30            TelemetryKind::Violation => write!(f, "violation"),
31        }
32    }
33}
34
35/// Данные телеметрии
36#[derive(Debug, Clone)]
37pub enum Telemetry {
38    /// Значение параметра
39    ParameterValue {
40        port: PortId,
41        parameter: ParameterId,
42        value: f32,
43        timestamp: u64,
44    },
45
46    /// Аудио данные
47    AudioData {
48        node_id: NodeId,
49        channel: usize,
50        data: Vec<f32>,
51        timestamp: u64,
52        sample_rate: f32,
53    },
54
55    /// Пиковое значение
56    Peak {
57        port: PortId,
58        value: f32,
59        timestamp: u64,
60        hold_time_ms: Option<u32>,
61    },
62
63    /// Событие
64    Event {
65        source: String,
66        kind: String,
67        data: Vec<f32>,
68        timestamp: u64,
69        description: Option<String>,
70    },
71
72    /// Нарушение микро-контроля
73    Violation {
74        component: String,
75        expected_ns: u64,
76        actual_ns: u64,
77        value: Option<f32>,
78        timestamp: u64,
79    },
80}
81
82// Реализуем трейт Command для Telemetry
83impl Command for Telemetry {}
84
85impl Telemetry {
86    /// Создать метку времени (текущее время в микросекундах)
87    pub fn now() -> u64 {
88        SystemTime::now()
89            .duration_since(UNIX_EPOCH)
90            .unwrap_or_default()
91            .as_micros() as u64
92    }
93
94    /// Создать телеметрию значения параметра
95    pub fn parameter(port: PortId, parameter: ParameterId, value: f32) -> Self {
96        Telemetry::ParameterValue {
97            port,
98            parameter,
99            value,
100            timestamp: Self::now(),
101        }
102    }
103
104    /// Создать телеметрию с указанной временной меткой (для тестов)
105    pub fn parameter_with_time(
106        port: PortId,
107        parameter: ParameterId,
108        value: f32,
109        timestamp: u64,
110    ) -> Self {
111        Telemetry::ParameterValue {
112            port,
113            parameter,
114            value,
115            timestamp,
116        }
117    }
118
119    /// Создать телеметрию аудиоданных
120    pub fn audio(node_id: NodeId, channel: usize, data: Vec<f32>) -> Self {
121        Telemetry::AudioData {
122            node_id,
123            channel,
124            data,
125            timestamp: Self::now(),
126            sample_rate: 44100.0,
127        }
128    }
129
130    /// Создать телеметрию аудиоданных с частотой дискретизации
131    pub fn audio_with_sample_rate(
132        node_id: NodeId,
133        channel: usize,
134        data: Vec<f32>,
135        sample_rate: f32,
136    ) -> Self {
137        Telemetry::AudioData {
138            node_id,
139            channel,
140            data,
141            timestamp: Self::now(),
142            sample_rate,
143        }
144    }
145
146    /// Создать телеметрию пика
147    pub fn peak(port: PortId, value: f32) -> Self {
148        Telemetry::Peak {
149            port,
150            value,
151            timestamp: Self::now(),
152            hold_time_ms: None,
153        }
154    }
155
156    /// Создать телеметрию пика с удержанием
157    pub fn peak_with_hold(port: PortId, value: f32, hold_time_ms: u32) -> Self {
158        Telemetry::Peak {
159            port,
160            value,
161            timestamp: Self::now(),
162            hold_time_ms: Some(hold_time_ms),
163        }
164    }
165
166    /// Создать телеметрию события
167    pub fn event(source: impl Into<String>, kind: impl Into<String>, data: Vec<f32>) -> Self {
168        Telemetry::Event {
169            source: source.into(),
170            kind: kind.into(),
171            data,
172            timestamp: Self::now(),
173            description: None,
174        }
175    }
176
177    /// Создать телеметрию события с описанием
178    pub fn event_with_description(
179        source: impl Into<String>,
180        kind: impl Into<String>,
181        data: Vec<f32>,
182        description: impl Into<String>,
183    ) -> Self {
184        Telemetry::Event {
185            source: source.into(),
186            kind: kind.into(),
187            data,
188            timestamp: Self::now(),
189            description: Some(description.into()),
190        }
191    }
192
193    /// Создать телеметрию нарушения
194    pub fn violation(
195        component: impl Into<String>,
196        expected_ns: u64,
197        actual_ns: u64,
198        value: Option<f32>,
199    ) -> Self {
200        Telemetry::Violation {
201            component: component.into(),
202            expected_ns,
203            actual_ns,
204            value,
205            timestamp: Self::now(),
206        }
207    }
208
209    /// Получить тип телеметрии
210    pub fn kind(&self) -> TelemetryKind {
211        match self {
212            Telemetry::ParameterValue { .. } => TelemetryKind::Parameter,
213            Telemetry::AudioData { .. } => TelemetryKind::Audio,
214            Telemetry::Peak { .. } => TelemetryKind::Peak,
215            Telemetry::Event { .. } => TelemetryKind::Event,
216            Telemetry::Violation { .. } => TelemetryKind::Violation,
217        }
218    }
219
220    /// Получить временную метку
221    pub fn timestamp(&self) -> u64 {
222        match self {
223            Telemetry::ParameterValue { timestamp, .. } => *timestamp,
224            Telemetry::AudioData { timestamp, .. } => *timestamp,
225            Telemetry::Peak { timestamp, .. } => *timestamp,
226            Telemetry::Event { timestamp, .. } => *timestamp,
227            Telemetry::Violation { timestamp, .. } => *timestamp,
228        }
229    }
230}
231
232impl fmt::Display for Telemetry {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        match self {
235            Telemetry::ParameterValue {
236                port,
237                parameter,
238                value,
239                timestamp,
240            } => {
241                write!(
242                    f,
243                    "[{}] 📊 {}::{} = {:.3}",
244                    timestamp, port, parameter, value
245                )
246            }
247            Telemetry::AudioData {
248                node_id,
249                channel,
250                data,
251                timestamp,
252                sample_rate,
253            } => {
254                let duration_ms = data.len() as f32 / sample_rate * 1000.0;
255                write!(
256                    f,
257                    "[{}] 🎵 {}:{} ({} samples, {:.1}ms)",
258                    timestamp,
259                    node_id,
260                    channel,
261                    data.len(),
262                    duration_ms
263                )
264            }
265            Telemetry::Peak {
266                port,
267                value,
268                timestamp,
269                hold_time_ms,
270            } => {
271                if let Some(hold) = hold_time_ms {
272                    write!(
273                        f,
274                        "[{}] 📈 {} = {:.3} (hold {}ms)",
275                        timestamp, port, value, hold
276                    )
277                } else {
278                    write!(f, "[{}] 📈 {} = {:.3}", timestamp, port, value)
279                }
280            }
281            Telemetry::Event {
282                source,
283                kind,
284                data,
285                timestamp,
286                description,
287            } => {
288                if let Some(desc) = description {
289                    write!(
290                        f,
291                        "[{}] 📢 {}:{} ({}) {:?}",
292                        timestamp, source, kind, desc, data
293                    )
294                } else {
295                    write!(f, "[{}] 📢 {}:{} {:?}", timestamp, source, kind, data)
296                }
297            }
298            Telemetry::Violation {
299                component,
300                expected_ns,
301                actual_ns,
302                value,
303                timestamp,
304            } => {
305                if let Some(v) = value {
306                    write!(
307                        f,
308                        "[{}] ⚠️ {} нарушение: {}нс > {}нс, value={:.3}",
309                        timestamp, component, actual_ns, expected_ns, v
310                    )
311                } else {
312                    write!(
313                        f,
314                        "[{}] ⚠️ {} нарушение: {}нс > {}нс",
315                        timestamp, component, actual_ns, expected_ns
316                    )
317                }
318            }
319        }
320    }
321}
322
323// TelemetryQueue - это просто тип-алиас на CommandQueue<Telemetry>
324pub type TelemetryQueue = super::command::CommandQueue<Telemetry>;
325
326// Удобные методы расширения для TelemetryQueue
327pub trait TelemetryQueueExt {
328    fn send_parameter(
329        &self,
330        port: PortId,
331        parameter: ParameterId,
332        value: f32,
333    ) -> Result<(), super::error::QueueError>;
334    fn send_audio(
335        &self,
336        node_id: NodeId,
337        channel: usize,
338        data: Vec<f32>,
339    ) -> Result<(), super::error::QueueError>;
340    fn send_audio_with_sample_rate(
341        &self,
342        node_id: NodeId,
343        channel: usize,
344        data: Vec<f32>,
345        sample_rate: f32,
346    ) -> Result<(), super::error::QueueError>;
347    fn send_peak(&self, port: PortId, value: f32) -> Result<(), super::error::QueueError>;
348    fn send_peak_with_hold(
349        &self,
350        port: PortId,
351        value: f32,
352        hold_time_ms: u32,
353    ) -> Result<(), super::error::QueueError>;
354    fn send_event(
355        &self,
356        source: &str,
357        kind: &str,
358        data: Vec<f32>,
359    ) -> Result<(), super::error::QueueError>;
360    fn send_event_with_description(
361        &self,
362        source: &str,
363        kind: &str,
364        data: Vec<f32>,
365        description: &str,
366    ) -> Result<(), super::error::QueueError>;
367    fn send_violation(
368        &self,
369        component: &str,
370        expected_ns: u64,
371        actual_ns: u64,
372        value: Option<f32>,
373    ) -> Result<(), super::error::QueueError>;
374}
375
376impl TelemetryQueueExt for super::command::CommandQueue<Telemetry> {
377    fn send_parameter(
378        &self,
379        port: PortId,
380        parameter: ParameterId,
381        value: f32,
382    ) -> Result<(), super::error::QueueError> {
383        self.send(Telemetry::parameter(port, parameter, value))
384            .map_err(|e| e.into())
385    }
386
387    fn send_audio(
388        &self,
389        node_id: NodeId,
390        channel: usize,
391        data: Vec<f32>,
392    ) -> Result<(), super::error::QueueError> {
393        self.send(Telemetry::audio(node_id, channel, data))
394            .map_err(|e| e.into())
395    }
396
397    fn send_audio_with_sample_rate(
398        &self,
399        node_id: NodeId,
400        channel: usize,
401        data: Vec<f32>,
402        sample_rate: f32,
403    ) -> Result<(), super::error::QueueError> {
404        self.send(Telemetry::audio_with_sample_rate(
405            node_id,
406            channel,
407            data,
408            sample_rate,
409        ))
410        .map_err(|e| e.into())
411    }
412
413    fn send_peak(&self, port: PortId, value: f32) -> Result<(), super::error::QueueError> {
414        self.send(Telemetry::peak(port, value))
415            .map_err(|e| e.into())
416    }
417
418    fn send_peak_with_hold(
419        &self,
420        port: PortId,
421        value: f32,
422        hold_time_ms: u32,
423    ) -> Result<(), super::error::QueueError> {
424        self.send(Telemetry::peak_with_hold(port, value, hold_time_ms))
425            .map_err(|e| e.into())
426    }
427
428    fn send_event(
429        &self,
430        source: &str,
431        kind: &str,
432        data: Vec<f32>,
433    ) -> Result<(), super::error::QueueError> {
434        self.send(Telemetry::event(source, kind, data))
435            .map_err(|e| e.into())
436    }
437
438    fn send_event_with_description(
439        &self,
440        source: &str,
441        kind: &str,
442        data: Vec<f32>,
443        description: &str,
444    ) -> Result<(), super::error::QueueError> {
445        self.send(Telemetry::event_with_description(
446            source,
447            kind,
448            data,
449            description,
450        ))
451        .map_err(|e| e.into())
452    }
453
454    fn send_violation(
455        &self,
456        component: &str,
457        expected_ns: u64,
458        actual_ns: u64,
459        value: Option<f32>,
460    ) -> Result<(), super::error::QueueError> {
461        self.send(Telemetry::violation(
462            component,
463            expected_ns,
464            actual_ns,
465            value,
466        ))
467        .map_err(|e| e.into())
468    }
469}