Skip to main content

rill_core/queues/
signal.rs

1//! Типы сигналов и команд для очередей
2//!
3//! Этот модуль определяет все типы команд, которые могут передаваться
4//! через очереди между компонентами Rill. Каждый тип команды
5//! представляет определенное действие или событие в системе.
6//!
7//! ## Иерархия команд
8//!
9//! - `Command` — общий тип-перечисление всех возможных команд
10//! - `SetParameter` — изменение параметра в AudioGraph
11//! - `AutomatonCommand` — управление автоматами
12//! - `SensorCommand` — управление сенсорами
13//! - `ServoCommand` — управление серво
14//!
15//! ## Пример
16//!
17//! ```rust
18//! use rill_core::queues::*;
19//! use rill_core::traits::*;
20//! #
21//! // Создаем очередь команд
22//! let queue: CommandQueue<CommandEnum> = CommandQueue::new("audio-control", 1024);
23//!
24//! // Создаем идентификаторы
25//! let node = NodeId(1);
26//! let port = PortId::control_in(node, 0);
27//! let param = ParameterId::new("gain").unwrap();
28//!
29//! // Где-то в мире автоматов
30//! let cmd = SetParameter::new(port, param, 0.5, SignalSource::Automaton("lfo".into()));
31//! queue.send(CommandEnum::SetParameter(cmd)).unwrap();
32//!
33//! // Где-то в звуковом мире
34//! while let Ok(cmd_enum) = queue.try_recv() {
35//!     if let CommandEnum::SetParameter(cmd) = cmd_enum {
36//!         // apply_parameter(cmd); // функция должна быть определена
37//!     }
38//! }
39//! # Ok::<(), Box<dyn std::error::Error>>(())
40//! ```
41
42use super::command::Command;
43use crate::traits::{ParameterId, PortId};
44use std::fmt;
45use std::time::{SystemTime, UNIX_EPOCH};
46
47//==============================================================================
48// SignalSource — источник сигнала
49//==============================================================================
50
51/// Источник сигнала (откуда пришла команда)
52///
53/// Используется для отслеживания происхождения команд и телеметрии.
54/// Позволяет реализовать защиту от обратной связи и отладку.
55/// Типы сигналов и команд для очередей
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57pub enum SignalSource {
58    Automaton(String),
59    Sensor(String),
60    Servo(String),
61    External(String),
62    Manual,
63    Script,
64}
65
66impl SignalSource {
67    pub fn name(&self) -> &str {
68        match self {
69            SignalSource::Automaton(name) => name,
70            SignalSource::Sensor(name) => name,
71            SignalSource::Servo(name) => name,
72            SignalSource::External(name) => name,
73            SignalSource::Manual => "manual",
74            SignalSource::Script => "script",
75        }
76    }
77
78    pub fn kind(&self) -> &'static str {
79        match self {
80            SignalSource::Automaton(_) => "automaton",
81            SignalSource::Sensor(_) => "sensor",
82            SignalSource::Servo(_) => "servo",
83            SignalSource::External(_) => "external",
84            SignalSource::Manual => "manual",
85            SignalSource::Script => "script",
86        }
87    }
88}
89
90impl fmt::Display for SignalSource {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            SignalSource::Automaton(name) => write!(f, "⚙️ {}", name),
94            SignalSource::Sensor(name) => write!(f, "👁️ {}", name),
95            SignalSource::Servo(name) => write!(f, "🦾 {}", name),
96            SignalSource::External(name) => write!(f, "🌍 {}", name),
97            SignalSource::Manual => write!(f, "👤 manual"),
98            SignalSource::Script => write!(f, "📜 script"),
99        }
100    }
101}
102
103// ===== SetParameter =====
104
105/// Команда изменения параметра
106#[derive(Debug, Clone)]
107pub struct SetParameter {
108    pub port: PortId,
109    pub parameter: ParameterId,
110    pub value: f32,
111    pub source: SignalSource,
112    pub timestamp: u64,
113}
114
115impl SetParameter {
116    pub fn new(port: PortId, parameter: ParameterId, value: f32, source: SignalSource) -> Self {
117        Self {
118            port,
119            parameter,
120            value,
121            source,
122            timestamp: Self::now(),
123        }
124    }
125
126    pub fn with_timestamp(
127        port: PortId,
128        parameter: ParameterId,
129        value: f32,
130        source: SignalSource,
131        timestamp: u64,
132    ) -> Self {
133        Self {
134            port,
135            parameter,
136            value,
137            source,
138            timestamp,
139        }
140    }
141
142    pub fn now() -> u64 {
143        SystemTime::now()
144            .duration_since(UNIX_EPOCH)
145            .unwrap_or_default()
146            .as_micros() as u64
147    }
148}
149
150impl PartialEq for SetParameter {
151    fn eq(&self, other: &Self) -> bool {
152        self.port == other.port
153            && self.parameter == other.parameter
154            && (self.value - other.value).abs() < f32::EPSILON
155            && self.source == other.source
156    }
157}
158
159impl fmt::Display for SetParameter {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        write!(
162            f,
163            "[{}] {} → {}::{} = {:.3}",
164            self.timestamp, self.source, self.port, self.parameter, self.value
165        )
166    }
167}
168
169// Реализуем трейт Command для SetParameter
170impl Command for SetParameter {}
171
172// ===== AutomatonCommand =====
173
174#[derive(Debug, Clone)]
175pub enum AutomatonCommand {
176    SetEnabled {
177        id: String,
178        enabled: bool,
179    },
180    SetParameter {
181        id: String,
182        name: String,
183        value: f32,
184    },
185    Reset {
186        id: String,
187    },
188    Connect {
189        from: String,
190        to: String,
191        gain: f32,
192    },
193    Disconnect {
194        from: String,
195        to: String,
196    },
197    Create {
198        kind: String,
199        id: String,
200        params: Vec<(String, f32)>,
201    },
202    Destroy {
203        id: String,
204    },
205}
206
207impl AutomatonCommand {
208    pub fn automaton_id(&self) -> Option<&str> {
209        match self {
210            AutomatonCommand::SetEnabled { id, .. } => Some(id),
211            AutomatonCommand::SetParameter { id, .. } => Some(id),
212            AutomatonCommand::Reset { id } => Some(id),
213            AutomatonCommand::Connect { from, to: _to, .. } => Some(from),
214            AutomatonCommand::Disconnect { from, to: _to } => Some(from),
215            AutomatonCommand::Create { id, .. } => Some(id),
216            AutomatonCommand::Destroy { id } => Some(id),
217        }
218    }
219}
220
221impl fmt::Display for AutomatonCommand {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        match self {
224            AutomatonCommand::SetEnabled { id, enabled } => {
225                write!(f, "Automaton[{}] set_enabled({})", id, enabled)
226            }
227            AutomatonCommand::SetParameter { id, name, value } => {
228                write!(f, "Automaton[{}] set_param({}={:.2})", id, name, value)
229            }
230            AutomatonCommand::Reset { id } => {
231                write!(f, "Automaton[{}] reset()", id)
232            }
233            AutomatonCommand::Connect { from, to, gain } => {
234                write!(f, "Automaton connect {} → {} gain={:.2}", from, to, gain)
235            }
236            AutomatonCommand::Disconnect { from, to } => {
237                write!(f, "Automaton disconnect {} → {}", from, to)
238            }
239            AutomatonCommand::Create { kind, id, params } => {
240                write!(
241                    f,
242                    "Automaton create {} as {} with {} params",
243                    kind,
244                    id,
245                    params.len()
246                )
247            }
248            AutomatonCommand::Destroy { id } => {
249                write!(f, "Automaton destroy {}", id)
250            }
251        }
252    }
253}
254
255impl Command for AutomatonCommand {}
256
257// ===== SensorCommand =====
258
259#[derive(Debug, Clone)]
260pub enum CalibrationKind {
261    Auto,
262    SetCurrentAsMin,
263    SetCurrentAsMax,
264    Reset,
265}
266
267impl fmt::Display for CalibrationKind {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        match self {
270            CalibrationKind::Auto => write!(f, "auto"),
271            CalibrationKind::SetCurrentAsMin => write!(f, "set_min"),
272            CalibrationKind::SetCurrentAsMax => write!(f, "set_max"),
273            CalibrationKind::Reset => write!(f, "reset"),
274        }
275    }
276}
277
278#[derive(Debug, Clone)]
279pub enum SensorCommand {
280    StartListening { id: String, source: String },
281    StopListening { id: String },
282    SetSensitivity { id: String, value: f32 },
283    Calibrate { id: String, kind: CalibrationKind },
284    SetEnabled { id: String, enabled: bool },
285}
286
287impl SensorCommand {
288    pub fn sensor_id(&self) -> &str {
289        match self {
290            SensorCommand::StartListening { id, .. } => id,
291            SensorCommand::StopListening { id } => id,
292            SensorCommand::SetSensitivity { id, .. } => id,
293            SensorCommand::Calibrate { id, .. } => id,
294            SensorCommand::SetEnabled { id, .. } => id,
295        }
296    }
297}
298
299impl fmt::Display for SensorCommand {
300    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301        match self {
302            SensorCommand::StartListening { id, source } => {
303                write!(f, "Sensor[{}] start listening to {}", id, source)
304            }
305            SensorCommand::StopListening { id } => {
306                write!(f, "Sensor[{}] stop listening", id)
307            }
308            SensorCommand::SetSensitivity { id, value } => {
309                write!(f, "Sensor[{}] set sensitivity to {:.2}", id, value)
310            }
311            SensorCommand::Calibrate { id, kind } => {
312                write!(f, "Sensor[{}] calibrate {}", id, kind)
313            }
314            SensorCommand::SetEnabled { id, enabled } => {
315                write!(f, "Sensor[{}] set enabled({})", id, enabled)
316            }
317        }
318    }
319}
320
321impl Command for SensorCommand {}
322
323// ===== ServoCommand =====
324
325#[derive(Debug, Clone)]
326pub enum MappingType {
327    Linear,
328    Exponential,
329    Logarithmic,
330    Inverted,
331    Custom(String),
332}
333
334impl fmt::Display for MappingType {
335    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336        match self {
337            MappingType::Linear => write!(f, "linear"),
338            MappingType::Exponential => write!(f, "exponential"),
339            MappingType::Logarithmic => write!(f, "logarithmic"),
340            MappingType::Inverted => write!(f, "inverted"),
341            MappingType::Custom(s) => write!(f, "custom({})", s),
342        }
343    }
344}
345
346#[derive(Debug, Clone)]
347pub enum ServoCommand {
348    BindToAutomaton {
349        servo_id: String,
350        automaton_id: String,
351    },
352    BindToParameter {
353        servo_id: String,
354        port: PortId,
355        parameter: ParameterId,
356    },
357    Unbind {
358        servo_id: String,
359    },
360    SetRange {
361        servo_id: String,
362        min: f32,
363        max: f32,
364    },
365    SetMapping {
366        servo_id: String,
367        mapping: MappingType,
368    },
369    SetEnabled {
370        servo_id: String,
371        enabled: bool,
372    },
373}
374
375impl ServoCommand {
376    pub fn servo_id(&self) -> &str {
377        match self {
378            ServoCommand::BindToAutomaton { servo_id, .. } => servo_id,
379            ServoCommand::BindToParameter { servo_id, .. } => servo_id,
380            ServoCommand::Unbind { servo_id } => servo_id,
381            ServoCommand::SetRange { servo_id, .. } => servo_id,
382            ServoCommand::SetMapping { servo_id, .. } => servo_id,
383            ServoCommand::SetEnabled { servo_id, .. } => servo_id,
384        }
385    }
386}
387
388impl fmt::Display for ServoCommand {
389    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390        match self {
391            ServoCommand::BindToAutomaton {
392                servo_id,
393                automaton_id,
394            } => {
395                write!(f, "Servo[{}] bind to automaton {}", servo_id, automaton_id)
396            }
397            ServoCommand::BindToParameter {
398                servo_id,
399                port,
400                parameter,
401            } => {
402                write!(f, "Servo[{}] bind to {}::{}", servo_id, port, parameter)
403            }
404            ServoCommand::Unbind { servo_id } => {
405                write!(f, "Servo[{}] unbind", servo_id)
406            }
407            ServoCommand::SetRange { servo_id, min, max } => {
408                write!(f, "Servo[{}] set range [{}, {}]", servo_id, min, max)
409            }
410            ServoCommand::SetMapping { servo_id, mapping } => {
411                write!(f, "Servo[{}] set mapping {}", servo_id, mapping)
412            }
413            ServoCommand::SetEnabled { servo_id, enabled } => {
414                write!(f, "Servo[{}] set enabled({})", servo_id, enabled)
415            }
416        }
417    }
418}
419
420impl Command for ServoCommand {}
421
422// ===== CommandType (бывшее Command) — общий тип команды =====
423
424/// Тип команды (для идентификации в рантайме)
425#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
426pub enum CommandType {
427    /// Изменение параметра
428    SetParameter,
429    /// Управление автоматом
430    Automaton,
431    /// Управление сенсором
432    Sensor,
433    /// Управление серво
434    Servo,
435    /// Системная команда
436    System,
437}
438
439impl fmt::Display for CommandType {
440    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441        match self {
442            CommandType::SetParameter => write!(f, "SetParameter"),
443            CommandType::Automaton => write!(f, "Automaton"),
444            CommandType::Sensor => write!(f, "Sensor"),
445            CommandType::Servo => write!(f, "Servo"),
446            CommandType::System => write!(f, "System"),
447        }
448    }
449}
450
451/// Общий тип команды, объединяющий все возможные команды
452///
453/// Полезен, когда нужно использовать одну очередь для всех типов команд,
454/// или когда тип команды неизвестен заранее.
455#[derive(Debug, Clone)]
456pub enum CommandEnum {
457    /// Изменение параметра
458    SetParameter(SetParameter),
459    /// Управление автоматом
460    Automaton(AutomatonCommand),
461    /// Управление сенсором
462    Sensor(SensorCommand),
463    /// Управление серво
464    Servo(ServoCommand),
465    /// Системная команда
466    System { kind: String, data: Vec<u8> },
467}
468
469impl CommandEnum {
470    /// Получить тип команды
471    pub fn command_type(&self) -> CommandType {
472        match self {
473            CommandEnum::SetParameter(_) => CommandType::SetParameter,
474            CommandEnum::Automaton(_) => CommandType::Automaton,
475            CommandEnum::Sensor(_) => CommandType::Sensor,
476            CommandEnum::Servo(_) => CommandType::Servo,
477            CommandEnum::System { .. } => CommandType::System,
478        }
479    }
480
481    /// Вернуть NodeId если команда адресована узлу графа (SetParameter).
482    pub fn target_node_id(&self) -> Option<crate::traits::NodeId> {
483        match self {
484            CommandEnum::SetParameter(cmd) => Some(cmd.port.node_id()),
485            _ => None,
486        }
487    }
488
489    /// Получить временную метку (если есть)
490    pub fn timestamp(&self) -> Option<u64> {
491        match self {
492            CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
493            _ => None,
494        }
495    }
496
497    /// Попытаться преобразовать в SetParameter
498    pub fn as_set_parameter(&self) -> Option<&SetParameter> {
499        match self {
500            CommandEnum::SetParameter(cmd) => Some(cmd),
501            _ => None,
502        }
503    }
504
505    /// Попытаться преобразовать в AutomatonCommand
506    pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
507        match self {
508            CommandEnum::Automaton(cmd) => Some(cmd),
509            _ => None,
510        }
511    }
512
513    /// Попытаться преобразовать в SensorCommand
514    pub fn as_sensor(&self) -> Option<&SensorCommand> {
515        match self {
516            CommandEnum::Sensor(cmd) => Some(cmd),
517            _ => None,
518        }
519    }
520
521    /// Попытаться преобразовать в ServoCommand
522    pub fn as_servo(&self) -> Option<&ServoCommand> {
523        match self {
524            CommandEnum::Servo(cmd) => Some(cmd),
525            _ => None,
526        }
527    }
528}
529
530impl fmt::Display for CommandEnum {
531    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532        match self {
533            CommandEnum::SetParameter(cmd) => write!(f, "{}", cmd),
534            CommandEnum::Automaton(cmd) => write!(f, "{}", cmd),
535            CommandEnum::Sensor(cmd) => write!(f, "{}", cmd),
536            CommandEnum::Servo(cmd) => write!(f, "{}", cmd),
537            CommandEnum::System { kind, data } => {
538                write!(f, "System[{}] ({} bytes)", kind, data.len())
539            }
540        }
541    }
542}
543
544// Реализуем трейт Command для CommandEnum, чтобы его можно было использовать в CommandQueue
545impl Command for CommandEnum {}
546
547// ===== Преобразования =====
548
549/// Маркерный трейт для типов, которые могут быть преобразованы в команду
550pub trait ToCommand: Send + 'static {
551    /// Тип команды, в которую преобразуется
552    type Command: Into<CommandEnum>;
553
554    /// Преобразовать в команду
555    fn to_command(self) -> Self::Command;
556}
557
558/// Маркерный трейт для типов, которые могут быть созданы из команды
559pub trait FromCommand: Sized {
560    /// Тип команды, из которой создается
561    type Command: TryInto<Self> + Clone;
562
563    /// Попытаться создать из команды
564    fn from_command(cmd: Self::Command) -> Option<Self>;
565}
566
567impl From<SetParameter> for CommandEnum {
568    fn from(cmd: SetParameter) -> Self {
569        CommandEnum::SetParameter(cmd)
570    }
571}
572
573impl From<AutomatonCommand> for CommandEnum {
574    fn from(cmd: AutomatonCommand) -> Self {
575        CommandEnum::Automaton(cmd)
576    }
577}
578
579impl From<SensorCommand> for CommandEnum {
580    fn from(cmd: SensorCommand) -> Self {
581        CommandEnum::Sensor(cmd)
582    }
583}
584
585impl From<ServoCommand> for CommandEnum {
586    fn from(cmd: ServoCommand) -> Self {
587        CommandEnum::Servo(cmd)
588    }
589}
590
591impl TryFrom<CommandEnum> for SetParameter {
592    type Error = ();
593
594    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
595        match cmd {
596            CommandEnum::SetParameter(cmd) => Ok(cmd),
597            _ => Err(()),
598        }
599    }
600}
601
602impl TryFrom<CommandEnum> for AutomatonCommand {
603    type Error = ();
604
605    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
606        match cmd {
607            CommandEnum::Automaton(cmd) => Ok(cmd),
608            _ => Err(()),
609        }
610    }
611}
612
613impl TryFrom<CommandEnum> for SensorCommand {
614    type Error = ();
615
616    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
617        match cmd {
618            CommandEnum::Sensor(cmd) => Ok(cmd),
619            _ => Err(()),
620        }
621    }
622}
623
624impl TryFrom<CommandEnum> for ServoCommand {
625    type Error = ();
626
627    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
628        match cmd {
629            CommandEnum::Servo(cmd) => Ok(cmd),
630            _ => Err(()),
631        }
632    }
633}