Skip to main content

rill_core/queues/
signal.rs

1//! Signal and command types for queues.
2//!
3//! This module defines all command types that can be sent through queues
4//! between Rill components. Each command type represents a specific
5//! action or event in the system.
6//!
7//! ## Command hierarchy
8//!
9//! - `CommandEnum` — top-level enum wrapping all command variants
10//! - `SetParameter` — parameter change for a signal graph node
11//! - `AutomatonCommand` — automaton control
12//! - `SensorCommand` — sensor control
13//! - `ServoCommand` — servo control
14//!
15//! ## Пример
16//!
17//! ```rust
18//! use rill_core::queues::*;
19//! use rill_core::traits::*;
20//! #
21//! // Создаем очередь команд
22//! let queue: CommandQueue<CommandEnum> = CommandQueue::new("signal-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/// Origin of a signal or command.
52///
53/// Used for tracking command provenance, feedback-loop prevention,
54/// and telemetry attribution.
55#[derive(Debug, Clone, PartialEq, Eq, Hash)]
56pub enum SignalSource {
57    /// Command from an automaton (LFO, envelope, sequencer).
58    Automaton(String),
59    /// Command from a sensor (physical input device).
60    Sensor(String),
61    /// Command from a servo (physical output device).
62    Servo(String),
63    /// Command from an external source (OSC, MIDI, etc.).
64    External(String),
65    /// Manual user interaction (UI slider, button, etc.).
66    Manual,
67    /// Command from a script.
68    Script,
69}
70
71impl SignalSource {
72    /// Return the human-readable name of this source.
73    pub fn name(&self) -> &str {
74        match self {
75            SignalSource::Automaton(name) => name,
76            SignalSource::Sensor(name) => name,
77            SignalSource::Servo(name) => name,
78            SignalSource::External(name) => name,
79            SignalSource::Manual => "manual",
80            SignalSource::Script => "script",
81        }
82    }
83
84    /// Return the type category of this source (e.g. "automaton", "sensor").
85    pub fn kind(&self) -> &'static str {
86        match self {
87            SignalSource::Automaton(_) => "automaton",
88            SignalSource::Sensor(_) => "sensor",
89            SignalSource::Servo(_) => "servo",
90            SignalSource::External(_) => "external",
91            SignalSource::Manual => "manual",
92            SignalSource::Script => "script",
93        }
94    }
95}
96
97impl fmt::Display for SignalSource {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        match self {
100            SignalSource::Automaton(name) => write!(f, "⚙️ {}", name),
101            SignalSource::Sensor(name) => write!(f, "👁️ {}", name),
102            SignalSource::Servo(name) => write!(f, "🦾 {}", name),
103            SignalSource::External(name) => write!(f, "🌍 {}", name),
104            SignalSource::Manual => write!(f, "👤 manual"),
105            SignalSource::Script => write!(f, "📜 script"),
106        }
107    }
108}
109
110// ===== SetParameter =====
111
112/// Command to change a parameter value on a signal graph node.
113#[derive(Debug, Clone)]
114pub struct SetParameter {
115    /// Target port.
116    pub port: PortId,
117    /// Target parameter identifier.
118    pub parameter: ParameterId,
119    /// New parameter value.
120    pub value: f32,
121    /// Origin of this command.
122    pub source: SignalSource,
123    /// Unix timestamp (microseconds).
124    pub timestamp: u64,
125}
126
127impl SetParameter {
128    /// Create a new parameter-change command with the current timestamp.
129    pub fn new(port: PortId, parameter: ParameterId, value: f32, source: SignalSource) -> Self {
130        Self {
131            port,
132            parameter,
133            value,
134            source,
135            timestamp: Self::now(),
136        }
137    }
138
139    /// Create a new parameter-change command with an explicit timestamp.
140    pub fn with_timestamp(
141        port: PortId,
142        parameter: ParameterId,
143        value: f32,
144        source: SignalSource,
145        timestamp: u64,
146    ) -> Self {
147        Self {
148            port,
149            parameter,
150            value,
151            source,
152            timestamp,
153        }
154    }
155
156    /// Return the current Unix time in microseconds.
157    pub fn now() -> u64 {
158        SystemTime::now()
159            .duration_since(UNIX_EPOCH)
160            .unwrap_or_default()
161            .as_micros() as u64
162    }
163}
164
165impl PartialEq for SetParameter {
166    fn eq(&self, other: &Self) -> bool {
167        self.port == other.port
168            && self.parameter == other.parameter
169            && (self.value - other.value).abs() < f32::EPSILON
170            && self.source == other.source
171    }
172}
173
174impl fmt::Display for SetParameter {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        write!(
177            f,
178            "[{}] {} → {}::{} = {:.3}",
179            self.timestamp, self.source, self.port, self.parameter, self.value
180        )
181    }
182}
183
184// Реализуем трейт Command для SetParameter
185impl Command for SetParameter {}
186
187// ===== AutomatonCommand =====
188
189/// Commands for controlling automata (LFOs, envelopes, sequencers).
190#[derive(Debug, Clone)]
191pub enum AutomatonCommand {
192    /// Enable or disable an automaton by ID.
193    SetEnabled {
194        /// Automaton identifier.
195        id: String,
196        /// Whether the automaton should be enabled.
197        enabled: bool,
198    },
199    /// Set a named parameter on an automaton.
200    SetParameter {
201        /// Automaton identifier.
202        id: String,
203        /// Parameter name.
204        name: String,
205        /// Parameter value.
206        value: f32,
207    },
208    /// Reset an automaton to its initial state.
209    Reset {
210        /// Automaton identifier.
211        id: String,
212    },
213    /// Connect an automaton output to another automaton input.
214    Connect {
215        /// Source automaton identifier.
216        from: String,
217        /// Destination automaton identifier.
218        to: String,
219        /// Connection gain.
220        gain: f32,
221    },
222    /// Disconnect two automata.
223    Disconnect {
224        /// Source automaton identifier.
225        from: String,
226        /// Destination automaton identifier.
227        to: String,
228    },
229    /// Create a new automaton instance.
230    Create {
231        /// Automaton type (e.g. "lfo", "envelope").
232        kind: String,
233        /// New automaton identifier.
234        id: String,
235        /// Initial parameter values.
236        params: Vec<(String, f32)>,
237    },
238    /// Destroy an automaton by ID.
239    Destroy {
240        /// Automaton identifier to remove.
241        id: String,
242    },
243}
244
245impl AutomatonCommand {
246    /// Return the target automaton ID, if applicable.
247    pub fn automaton_id(&self) -> Option<&str> {
248        match self {
249            AutomatonCommand::SetEnabled { id, .. } => Some(id),
250            AutomatonCommand::SetParameter { id, .. } => Some(id),
251            AutomatonCommand::Reset { id } => Some(id),
252            AutomatonCommand::Connect { from, to: _to, .. } => Some(from),
253            AutomatonCommand::Disconnect { from, to: _to } => Some(from),
254            AutomatonCommand::Create { id, .. } => Some(id),
255            AutomatonCommand::Destroy { id } => Some(id),
256        }
257    }
258}
259
260impl fmt::Display for AutomatonCommand {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        match self {
263            AutomatonCommand::SetEnabled { id, enabled } => {
264                write!(f, "Automaton[{}] set_enabled({})", id, enabled)
265            }
266            AutomatonCommand::SetParameter { id, name, value } => {
267                write!(f, "Automaton[{}] set_param({}={:.2})", id, name, value)
268            }
269            AutomatonCommand::Reset { id } => {
270                write!(f, "Automaton[{}] reset()", id)
271            }
272            AutomatonCommand::Connect { from, to, gain } => {
273                write!(f, "Automaton connect {} → {} gain={:.2}", from, to, gain)
274            }
275            AutomatonCommand::Disconnect { from, to } => {
276                write!(f, "Automaton disconnect {} → {}", from, to)
277            }
278            AutomatonCommand::Create { kind, id, params } => {
279                write!(
280                    f,
281                    "Automaton create {} as {} with {} params",
282                    kind,
283                    id,
284                    params.len()
285                )
286            }
287            AutomatonCommand::Destroy { id } => {
288                write!(f, "Automaton destroy {}", id)
289            }
290        }
291    }
292}
293
294impl Command for AutomatonCommand {}
295
296// ===== SensorCommand =====
297
298/// Type of sensor calibration to perform.
299#[derive(Debug, Clone)]
300pub enum CalibrationKind {
301    /// Automatically determine min/max from signal range.
302    Auto,
303    /// Set the current sensor reading as the minimum value.
304    SetCurrentAsMin,
305    /// Set the current sensor reading as the maximum value.
306    SetCurrentAsMax,
307    /// Reset calibration to factory defaults.
308    Reset,
309}
310
311impl fmt::Display for CalibrationKind {
312    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313        match self {
314            CalibrationKind::Auto => write!(f, "auto"),
315            CalibrationKind::SetCurrentAsMin => write!(f, "set_min"),
316            CalibrationKind::SetCurrentAsMax => write!(f, "set_max"),
317            CalibrationKind::Reset => write!(f, "reset"),
318        }
319    }
320}
321
322/// Commands for controlling sensors (physical input devices).
323#[derive(Debug, Clone)]
324pub enum SensorCommand {
325    /// Start listening to a sensor data source.
326    StartListening {
327        /// Sensor identifier.
328        id: String,
329        /// Data source to listen to.
330        source: String,
331    },
332    /// Stop listening to a sensor.
333    StopListening {
334        /// Sensor identifier.
335        id: String,
336    },
337    /// Set sensor sensitivity.
338    SetSensitivity {
339        /// Sensor identifier.
340        id: String,
341        /// Sensitivity value.
342        value: f32,
343    },
344    /// Calibrate a sensor.
345    Calibrate {
346        /// Sensor identifier.
347        id: String,
348        /// Calibration type.
349        kind: CalibrationKind,
350    },
351    /// Enable or disable a sensor.
352    SetEnabled {
353        /// Sensor identifier.
354        id: String,
355        /// Whether the sensor should be enabled.
356        enabled: bool,
357    },
358}
359
360impl SensorCommand {
361    /// Return the target sensor ID.
362    pub fn sensor_id(&self) -> &str {
363        match self {
364            SensorCommand::StartListening { id, .. } => id,
365            SensorCommand::StopListening { id } => id,
366            SensorCommand::SetSensitivity { id, .. } => id,
367            SensorCommand::Calibrate { id, .. } => id,
368            SensorCommand::SetEnabled { id, .. } => id,
369        }
370    }
371}
372
373impl fmt::Display for SensorCommand {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        match self {
376            SensorCommand::StartListening { id, source } => {
377                write!(f, "Sensor[{}] start listening to {}", id, source)
378            }
379            SensorCommand::StopListening { id } => {
380                write!(f, "Sensor[{}] stop listening", id)
381            }
382            SensorCommand::SetSensitivity { id, value } => {
383                write!(f, "Sensor[{}] set sensitivity to {:.2}", id, value)
384            }
385            SensorCommand::Calibrate { id, kind } => {
386                write!(f, "Sensor[{}] calibrate {}", id, kind)
387            }
388            SensorCommand::SetEnabled { id, enabled } => {
389                write!(f, "Sensor[{}] set enabled({})", id, enabled)
390            }
391        }
392    }
393}
394
395impl Command for SensorCommand {}
396
397// ===== ServoCommand =====
398
399/// Mapping function type for servo output value transformation.
400#[derive(Debug, Clone)]
401pub enum MappingType {
402    /// Linear mapping (identity).
403    Linear,
404    /// Exponential mapping.
405    Exponential,
406    /// Logarithmic mapping.
407    Logarithmic,
408    /// Inverted (reverse) mapping.
409    Inverted,
410    /// Custom named mapping function.
411    Custom(String),
412}
413
414impl fmt::Display for MappingType {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        match self {
417            MappingType::Linear => write!(f, "linear"),
418            MappingType::Exponential => write!(f, "exponential"),
419            MappingType::Logarithmic => write!(f, "logarithmic"),
420            MappingType::Inverted => write!(f, "inverted"),
421            MappingType::Custom(s) => write!(f, "custom({})", s),
422        }
423    }
424}
425
426/// Commands for controlling servos (physical output devices).
427#[derive(Debug, Clone)]
428pub enum ServoCommand {
429    /// Bind a servo to follow an automaton output.
430    BindToAutomaton {
431        /// Servo identifier.
432        servo_id: String,
433        /// Automaton identifier to bind to.
434        automaton_id: String,
435    },
436    /// Bind a servo directly to a signal graph parameter.
437    BindToParameter {
438        /// Servo identifier.
439        servo_id: String,
440        /// Target port.
441        port: PortId,
442        /// Target parameter.
443        parameter: ParameterId,
444    },
445    /// Unbind a servo from all sources.
446    Unbind {
447        /// Servo identifier.
448        servo_id: String,
449    },
450    /// Set the output range of a servo.
451    SetRange {
452        /// Servo identifier.
453        servo_id: String,
454        /// Minimum output value.
455        min: f32,
456        /// Maximum output value.
457        max: f32,
458    },
459    /// Set the value mapping function for a servo.
460    SetMapping {
461        /// Servo identifier.
462        servo_id: String,
463        /// Mapping type.
464        mapping: MappingType,
465    },
466    /// Enable or disable a servo.
467    SetEnabled {
468        /// Servo identifier.
469        servo_id: String,
470        /// Whether the servo should be enabled.
471        enabled: bool,
472    },
473}
474
475impl ServoCommand {
476    /// Return the target servo ID.
477    pub fn servo_id(&self) -> &str {
478        match self {
479            ServoCommand::BindToAutomaton { servo_id, .. } => servo_id,
480            ServoCommand::BindToParameter { servo_id, .. } => servo_id,
481            ServoCommand::Unbind { servo_id } => servo_id,
482            ServoCommand::SetRange { servo_id, .. } => servo_id,
483            ServoCommand::SetMapping { servo_id, .. } => servo_id,
484            ServoCommand::SetEnabled { servo_id, .. } => servo_id,
485        }
486    }
487}
488
489impl fmt::Display for ServoCommand {
490    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491        match self {
492            ServoCommand::BindToAutomaton {
493                servo_id,
494                automaton_id,
495            } => {
496                write!(f, "Servo[{}] bind to automaton {}", servo_id, automaton_id)
497            }
498            ServoCommand::BindToParameter {
499                servo_id,
500                port,
501                parameter,
502            } => {
503                write!(f, "Servo[{}] bind to {}::{}", servo_id, port, parameter)
504            }
505            ServoCommand::Unbind { servo_id } => {
506                write!(f, "Servo[{}] unbind", servo_id)
507            }
508            ServoCommand::SetRange { servo_id, min, max } => {
509                write!(f, "Servo[{}] set range [{}, {}]", servo_id, min, max)
510            }
511            ServoCommand::SetMapping { servo_id, mapping } => {
512                write!(f, "Servo[{}] set mapping {}", servo_id, mapping)
513            }
514            ServoCommand::SetEnabled { servo_id, enabled } => {
515                write!(f, "Servo[{}] set enabled({})", servo_id, enabled)
516            }
517        }
518    }
519}
520
521impl Command for ServoCommand {}
522
523// ===== CommandType (бывшее Command) — общий тип команды =====
524
525/// Runtime command type identifier.
526#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
527pub enum CommandType {
528    /// Parameter change command.
529    SetParameter,
530    /// Automaton control command.
531    Automaton,
532    /// Sensor control command.
533    Sensor,
534    /// Servo control command.
535    Servo,
536    /// System command.
537    System,
538}
539
540impl fmt::Display for CommandType {
541    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542        match self {
543            CommandType::SetParameter => write!(f, "SetParameter"),
544            CommandType::Automaton => write!(f, "Automaton"),
545            CommandType::Sensor => write!(f, "Sensor"),
546            CommandType::Servo => write!(f, "Servo"),
547            CommandType::System => write!(f, "System"),
548        }
549    }
550}
551
552/// Universal command enum combining all possible command types.
553///
554/// Useful when a single queue must transport multiple command types,
555/// or when the command type is not known ahead of time.
556#[derive(Debug, Clone)]
557pub enum CommandEnum {
558    /// Parameter change command.
559    SetParameter(SetParameter),
560    /// Automaton control command.
561    Automaton(AutomatonCommand),
562    /// Sensor control command.
563    Sensor(SensorCommand),
564    /// Servo control command.
565    Servo(ServoCommand),
566    /// System-level command with opaque payload.
567    System {
568        /// System command kind.
569        kind: String,
570        /// Opaque command data.
571        data: Vec<u8>,
572    },
573}
574
575impl CommandEnum {
576    /// Return the runtime type tag of this command.
577    pub fn command_type(&self) -> CommandType {
578        match self {
579            CommandEnum::SetParameter(_) => CommandType::SetParameter,
580            CommandEnum::Automaton(_) => CommandType::Automaton,
581            CommandEnum::Sensor(_) => CommandType::Sensor,
582            CommandEnum::Servo(_) => CommandType::Servo,
583            CommandEnum::System { .. } => CommandType::System,
584        }
585    }
586
587    /// If this is a `SetParameter` command, return the target `NodeId`.
588    pub fn target_node_id(&self) -> Option<crate::traits::NodeId> {
589        match self {
590            CommandEnum::SetParameter(cmd) => Some(cmd.port.node_id()),
591            _ => None,
592        }
593    }
594
595    /// Return the timestamp if the command carries one.
596    pub fn timestamp(&self) -> Option<u64> {
597        match self {
598            CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
599            _ => None,
600        }
601    }
602
603    /// Try to downcast to `SetParameter`.
604    pub fn as_set_parameter(&self) -> Option<&SetParameter> {
605        match self {
606            CommandEnum::SetParameter(cmd) => Some(cmd),
607            _ => None,
608        }
609    }
610
611    /// Try to downcast to `AutomatonCommand`.
612    pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
613        match self {
614            CommandEnum::Automaton(cmd) => Some(cmd),
615            _ => None,
616        }
617    }
618
619    /// Try to downcast to `SensorCommand`.
620    pub fn as_sensor(&self) -> Option<&SensorCommand> {
621        match self {
622            CommandEnum::Sensor(cmd) => Some(cmd),
623            _ => None,
624        }
625    }
626
627    /// Try to downcast to `ServoCommand`.
628    pub fn as_servo(&self) -> Option<&ServoCommand> {
629        match self {
630            CommandEnum::Servo(cmd) => Some(cmd),
631            _ => None,
632        }
633    }
634}
635
636impl fmt::Display for CommandEnum {
637    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638        match self {
639            CommandEnum::SetParameter(cmd) => write!(f, "{}", cmd),
640            CommandEnum::Automaton(cmd) => write!(f, "{}", cmd),
641            CommandEnum::Sensor(cmd) => write!(f, "{}", cmd),
642            CommandEnum::Servo(cmd) => write!(f, "{}", cmd),
643            CommandEnum::System { kind, data } => {
644                write!(f, "System[{}] ({} bytes)", kind, data.len())
645            }
646        }
647    }
648}
649
650// Реализуем трейт Command для CommandEnum, чтобы его можно было использовать в CommandQueue
651impl Command for CommandEnum {}
652
653// ===== Преобразования =====
654
655/// Marker trait for types that can be converted into a command.
656pub trait ToCommand: Send + 'static {
657    /// The command type this type converts into.
658    type Command: Into<CommandEnum>;
659
660    /// Convert self into a command.
661    fn to_command(self) -> Self::Command;
662}
663
664/// Marker trait for types that can be constructed from a command.
665pub trait FromCommand: Sized {
666    /// The command type this type is constructed from.
667    type Command: TryInto<Self> + Clone;
668
669    /// Try to construct from a command.
670    fn from_command(cmd: Self::Command) -> Option<Self>;
671}
672
673impl From<SetParameter> for CommandEnum {
674    fn from(cmd: SetParameter) -> Self {
675        CommandEnum::SetParameter(cmd)
676    }
677}
678
679impl From<AutomatonCommand> for CommandEnum {
680    fn from(cmd: AutomatonCommand) -> Self {
681        CommandEnum::Automaton(cmd)
682    }
683}
684
685impl From<SensorCommand> for CommandEnum {
686    fn from(cmd: SensorCommand) -> Self {
687        CommandEnum::Sensor(cmd)
688    }
689}
690
691impl From<ServoCommand> for CommandEnum {
692    fn from(cmd: ServoCommand) -> Self {
693        CommandEnum::Servo(cmd)
694    }
695}
696
697impl TryFrom<CommandEnum> for SetParameter {
698    type Error = ();
699
700    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
701        match cmd {
702            CommandEnum::SetParameter(cmd) => Ok(cmd),
703            _ => Err(()),
704        }
705    }
706}
707
708impl TryFrom<CommandEnum> for AutomatonCommand {
709    type Error = ();
710
711    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
712        match cmd {
713            CommandEnum::Automaton(cmd) => Ok(cmd),
714            _ => Err(()),
715        }
716    }
717}
718
719impl TryFrom<CommandEnum> for SensorCommand {
720    type Error = ();
721
722    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
723        match cmd {
724            CommandEnum::Sensor(cmd) => Ok(cmd),
725            _ => Err(()),
726        }
727    }
728}
729
730impl TryFrom<CommandEnum> for ServoCommand {
731    type Error = ();
732
733    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
734        match cmd {
735            CommandEnum::Servo(cmd) => Ok(cmd),
736            _ => Err(()),
737        }
738    }
739}