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//! ## Example
16//!
17//!
18//! See crate-level documentation for usage examples.
19
20use super::command::Command;
21use super::control_event::ControlEvent;
22use crate::time::ClockTick;
23use crate::traits::{ParamValue, ParameterId};
24use std::fmt;
25use std::time::{SystemTime, UNIX_EPOCH};
26
27//==============================================================================
28// SignalOrigin — signal source
29//==============================================================================
30
31/// Origin of a signal or command.
32///
33/// Used for tracking command provenance, feedback-loop prevention,
34/// and telemetry attribution.
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub enum SignalOrigin {
37    /// Command from an automaton (LFO, envelope, sequencer).
38    Automaton(String),
39    /// Command from a sensor (physical input device).
40    Sensor(String),
41    /// Command from a servo (physical output device).
42    Servo(String),
43    /// Command from an external source (OSC, etc.).
44    External(String),
45    /// Manual user interaction (UI slider, button, etc.).
46    Manual,
47    /// Command from a script.
48    Script,
49}
50
51impl SignalOrigin {
52    /// Return the human-readable name of this source.
53    pub fn name(&self) -> &str {
54        match self {
55            SignalOrigin::Automaton(name) => name,
56            SignalOrigin::Sensor(name) => name,
57            SignalOrigin::Servo(name) => name,
58            SignalOrigin::External(name) => name,
59            SignalOrigin::Manual => "manual",
60            SignalOrigin::Script => "script",
61        }
62    }
63
64    /// Return the type category of this source (e.g. "automaton", "sensor").
65    pub fn kind(&self) -> &'static str {
66        match self {
67            SignalOrigin::Automaton(_) => "automaton",
68            SignalOrigin::Sensor(_) => "sensor",
69            SignalOrigin::Servo(_) => "servo",
70            SignalOrigin::External(_) => "external",
71            SignalOrigin::Manual => "manual",
72            SignalOrigin::Script => "script",
73        }
74    }
75}
76
77impl fmt::Display for SignalOrigin {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            SignalOrigin::Automaton(name) => write!(f, "⚙️ {}", name),
81            SignalOrigin::Sensor(name) => write!(f, "👁️ {}", name),
82            SignalOrigin::Servo(name) => write!(f, "🦾 {}", name),
83            SignalOrigin::External(name) => write!(f, "🌍 {}", name),
84            SignalOrigin::Manual => write!(f, "👤 manual"),
85            SignalOrigin::Script => write!(f, "📜 script"),
86        }
87    }
88}
89
90// ===== SetParameter =====
91
92/// Command to change a parameter value on a signal graph node.
93#[derive(Debug, Clone)]
94pub struct SetParameter {
95    /// Target port.
96    pub port: String,
97    /// Node anchor name in the graph IR (for lang-based graphs).
98    pub anchor: String,
99    /// Target parameter identifier.
100    pub parameter: ParameterId,
101    /// New parameter value.
102    pub value: ParamValue,
103    /// Origin of this command.
104    pub source: SignalOrigin,
105    /// Unix timestamp (microseconds).
106    pub timestamp: u64,
107    /// Optional sample-accurate application time (absolute sample position).
108    ///
109    /// When `Some(pos)`, the graph applies this change during the processing
110    /// block whose sample range contains `pos`, rather than immediately on
111    /// drain. This lets tick-driven producers (sequencers, servos) place
112    /// parameter changes at exact sample positions instead of being subject to
113    /// how the backend batches blocks per I/O callback. `None` = apply as soon
114    /// as it is drained (legacy behaviour).
115    pub sample_pos: Option<u64>,
116}
117
118impl SetParameter {
119    /// Create a new parameter-change command with the current timestamp.
120    pub fn new(
121        port: String,
122        parameter: ParameterId,
123        value: ParamValue,
124        source: SignalOrigin,
125    ) -> Self {
126        Self {
127            port,
128            anchor: String::new(),
129            parameter,
130            value,
131            source,
132            timestamp: Self::now(),
133            sample_pos: None,
134        }
135    }
136
137    /// Create a new parameter-change command with an explicit timestamp.
138    pub fn with_timestamp(
139        port: String,
140        parameter: ParameterId,
141        value: ParamValue,
142        source: SignalOrigin,
143        timestamp: u64,
144    ) -> Self {
145        Self {
146            port,
147            anchor: String::new(),
148            parameter,
149            value,
150            source,
151            timestamp,
152            sample_pos: None,
153        }
154    }
155
156    /// Set the sample-accurate application time (absolute sample position).
157    pub fn with_sample_pos(mut self, sample_pos: u64) -> Self {
158        self.sample_pos = Some(sample_pos);
159        self
160    }
161
162    /// Return the current Unix time in microseconds.
163    pub fn now() -> u64 {
164        SystemTime::now()
165            .duration_since(UNIX_EPOCH)
166            .unwrap_or_default()
167            .as_micros() as u64
168    }
169}
170
171impl PartialEq for SetParameter {
172    fn eq(&self, other: &Self) -> bool {
173        self.port == other.port
174            && self.anchor == other.anchor
175            && self.parameter == other.parameter
176            && self.value == other.value
177            && self.source == other.source
178    }
179}
180
181impl fmt::Display for SetParameter {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        write!(
184            f,
185            "[{}] {} → {}::{} = {:?}",
186            self.timestamp, self.source, self.port, self.parameter, self.value
187        )
188    }
189}
190
191// Implement the Command trait for SetParameter
192impl Command for SetParameter {}
193
194// ===== AutomatonCommand =====
195
196/// Commands for controlling automatons (LFOs, envelopes, sequencers).
197#[derive(Debug, Clone)]
198pub enum AutomatonCommand {
199    /// Enable or disable an automaton by ID.
200    SetEnabled {
201        /// Automaton identifier.
202        id: String,
203        /// Whether the automaton should be enabled.
204        enabled: bool,
205    },
206    /// Set a named parameter on an automaton.
207    SetParameter {
208        /// Automaton identifier.
209        id: String,
210        /// Parameter name.
211        name: String,
212        /// Parameter value.
213        value: f32,
214    },
215    /// Reset an automaton to its initial state.
216    Reset {
217        /// Automaton identifier.
218        id: String,
219    },
220    /// Connect an automaton output to another automaton input.
221    Connect {
222        /// Source automaton identifier.
223        from: String,
224        /// Destination automaton identifier.
225        to: String,
226        /// Connection gain.
227        gain: f32,
228    },
229    /// Disconnect two automatons.
230    Disconnect {
231        /// Source automaton identifier.
232        from: String,
233        /// Destination automaton identifier.
234        to: String,
235    },
236    /// Create a new automaton instance.
237    Create {
238        /// Automaton type (e.g. "lfo", "envelope").
239        kind: String,
240        /// New automaton identifier.
241        id: String,
242        /// Initial parameter values.
243        params: Vec<(String, f32)>,
244    },
245    /// Destroy an automaton by ID.
246    Destroy {
247        /// Automaton identifier to remove.
248        id: String,
249    },
250    /// Wake the automaton to process a clock tick (no payload required).
251    Wake {
252        /// Automaton identifier.
253        id: String,
254    },
255    /// Set a value from UI input for conflict resolution.
256    UiValue {
257        /// Automaton identifier.
258        id: String,
259        /// Raw value from UI.
260        value: f64,
261    },
262    /// Release UI control (unfreeze in TouchOverride mode).
263    UiRelease {
264        /// Automaton identifier.
265        id: String,
266    },
267}
268
269impl AutomatonCommand {
270    /// Return the target automaton ID, if applicable.
271    pub fn automaton_id(&self) -> Option<&str> {
272        match self {
273            AutomatonCommand::SetEnabled { id, .. } => Some(id),
274            AutomatonCommand::SetParameter { id, .. } => Some(id),
275            AutomatonCommand::Reset { id } => Some(id),
276            AutomatonCommand::Connect { from, to: _to, .. } => Some(from),
277            AutomatonCommand::Disconnect { from, to: _to } => Some(from),
278            AutomatonCommand::Create { id, .. } => Some(id),
279            AutomatonCommand::Destroy { id } => Some(id),
280            AutomatonCommand::Wake { id } => Some(id),
281            AutomatonCommand::UiValue { id, .. } => Some(id),
282            AutomatonCommand::UiRelease { id } => Some(id),
283        }
284    }
285}
286
287impl fmt::Display for AutomatonCommand {
288    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289        match self {
290            AutomatonCommand::SetEnabled { id, enabled } => {
291                write!(f, "Automaton[{}] set_enabled({})", id, enabled)
292            }
293            AutomatonCommand::SetParameter { id, name, value } => {
294                write!(f, "Automaton[{}] set_param({}={:.2})", id, name, value)
295            }
296            AutomatonCommand::Reset { id } => {
297                write!(f, "Automaton[{}] reset()", id)
298            }
299            AutomatonCommand::Connect { from, to, gain } => {
300                write!(f, "Automaton connect {} → {} gain={:.2}", from, to, gain)
301            }
302            AutomatonCommand::Disconnect { from, to } => {
303                write!(f, "Automaton disconnect {} → {}", from, to)
304            }
305            AutomatonCommand::Create { kind, id, params } => {
306                write!(
307                    f,
308                    "Automaton create {} as {} with {} params",
309                    kind,
310                    id,
311                    params.len()
312                )
313            }
314            AutomatonCommand::Destroy { id } => {
315                write!(f, "Automaton destroy {}", id)
316            }
317            AutomatonCommand::Wake { id } => {
318                write!(f, "Automaton[{}] wake(tick)", id)
319            }
320            AutomatonCommand::UiValue { id, value } => {
321                write!(f, "Automaton[{}] ui_value({:.2})", id, value)
322            }
323            AutomatonCommand::UiRelease { id } => {
324                write!(f, "Automaton[{}] ui_release()", id)
325            }
326        }
327    }
328}
329
330impl Command for AutomatonCommand {}
331
332// ===== SensorCommand =====
333
334/// Type of sensor calibration to perform.
335#[derive(Debug, Clone)]
336pub enum CalibrationKind {
337    /// Automatically determine min/max from signal range.
338    Auto,
339    /// Set the current sensor reading as the minimum value.
340    SetCurrentAsMin,
341    /// Set the current sensor reading as the maximum value.
342    SetCurrentAsMax,
343    /// Reset calibration to factory defaults.
344    Reset,
345}
346
347impl fmt::Display for CalibrationKind {
348    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349        match self {
350            CalibrationKind::Auto => write!(f, "auto"),
351            CalibrationKind::SetCurrentAsMin => write!(f, "set_min"),
352            CalibrationKind::SetCurrentAsMax => write!(f, "set_max"),
353            CalibrationKind::Reset => write!(f, "reset"),
354        }
355    }
356}
357
358/// Commands for controlling sensors (physical input devices).
359#[derive(Debug, Clone)]
360pub enum SensorCommand {
361    /// Start listening to a sensor data source.
362    StartListening {
363        /// Sensor identifier.
364        id: String,
365        /// Data source to listen to.
366        source: String,
367    },
368    /// Stop listening to a sensor.
369    StopListening {
370        /// Sensor identifier.
371        id: String,
372    },
373    /// Set sensor sensitivity.
374    SetSensitivity {
375        /// Sensor identifier.
376        id: String,
377        /// Sensitivity value.
378        value: f32,
379    },
380    /// Calibrate a sensor.
381    Calibrate {
382        /// Sensor identifier.
383        id: String,
384        /// Calibration type.
385        kind: CalibrationKind,
386    },
387    /// Enable or disable a sensor.
388    SetEnabled {
389        /// Sensor identifier.
390        id: String,
391        /// Whether the sensor should be enabled.
392        enabled: bool,
393    },
394}
395
396impl SensorCommand {
397    /// Return the target sensor ID.
398    pub fn sensor_id(&self) -> &str {
399        match self {
400            SensorCommand::StartListening { id, .. } => id,
401            SensorCommand::StopListening { id } => id,
402            SensorCommand::SetSensitivity { id, .. } => id,
403            SensorCommand::Calibrate { id, .. } => id,
404            SensorCommand::SetEnabled { id, .. } => id,
405        }
406    }
407}
408
409impl fmt::Display for SensorCommand {
410    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411        match self {
412            SensorCommand::StartListening { id, source } => {
413                write!(f, "Sensor[{}] start listening to {}", id, source)
414            }
415            SensorCommand::StopListening { id } => {
416                write!(f, "Sensor[{}] stop listening", id)
417            }
418            SensorCommand::SetSensitivity { id, value } => {
419                write!(f, "Sensor[{}] set sensitivity to {:.2}", id, value)
420            }
421            SensorCommand::Calibrate { id, kind } => {
422                write!(f, "Sensor[{}] calibrate {}", id, kind)
423            }
424            SensorCommand::SetEnabled { id, enabled } => {
425                write!(f, "Sensor[{}] set enabled({})", id, enabled)
426            }
427        }
428    }
429}
430
431impl Command for SensorCommand {}
432
433// ===== ServoCommand =====
434
435/// Mapping function type for servo output value transformation.
436#[derive(Debug, Clone)]
437pub enum MappingType {
438    /// Linear mapping (identity).
439    Linear,
440    /// Exponential mapping.
441    Exponential,
442    /// Logarithmic mapping.
443    Logarithmic,
444    /// Inverted (reverse) mapping.
445    Inverted,
446    /// Custom named mapping function.
447    Custom(String),
448}
449
450impl fmt::Display for MappingType {
451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452        match self {
453            MappingType::Linear => write!(f, "linear"),
454            MappingType::Exponential => write!(f, "exponential"),
455            MappingType::Logarithmic => write!(f, "logarithmic"),
456            MappingType::Inverted => write!(f, "inverted"),
457            MappingType::Custom(s) => write!(f, "custom({})", s),
458        }
459    }
460}
461
462/// Commands for controlling servos (physical output devices).
463#[derive(Debug, Clone)]
464pub enum ServoCommand {
465    /// Bind a servo to follow an automaton output.
466    BindToAutomaton {
467        /// Servo identifier.
468        servo_id: String,
469        /// Automaton identifier to bind to.
470        automaton_id: String,
471    },
472    /// Bind a servo directly to a signal graph parameter.
473    BindToParameter {
474        /// Servo identifier.
475        servo_id: String,
476        /// Target port.
477        port: String,
478        /// Target parameter.
479        parameter: ParameterId,
480    },
481    /// Unbind a servo from all sources.
482    Unbind {
483        /// Servo identifier.
484        servo_id: String,
485    },
486    /// Set the output range of a servo.
487    SetRange {
488        /// Servo identifier.
489        servo_id: String,
490        /// Minimum output value.
491        min: f32,
492        /// Maximum output value.
493        max: f32,
494    },
495    /// Set the value mapping function for a servo.
496    SetMapping {
497        /// Servo identifier.
498        servo_id: String,
499        /// Mapping type.
500        mapping: MappingType,
501    },
502    /// Enable or disable a servo.
503    SetEnabled {
504        /// Servo identifier.
505        servo_id: String,
506        /// Whether the servo should be enabled.
507        enabled: bool,
508    },
509}
510
511impl ServoCommand {
512    /// Return the target servo ID.
513    pub fn servo_id(&self) -> &str {
514        match self {
515            ServoCommand::BindToAutomaton { servo_id, .. } => servo_id,
516            ServoCommand::BindToParameter { servo_id, .. } => servo_id,
517            ServoCommand::Unbind { servo_id } => servo_id,
518            ServoCommand::SetRange { servo_id, .. } => servo_id,
519            ServoCommand::SetMapping { servo_id, .. } => servo_id,
520            ServoCommand::SetEnabled { servo_id, .. } => servo_id,
521        }
522    }
523}
524
525impl fmt::Display for ServoCommand {
526    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527        match self {
528            ServoCommand::BindToAutomaton {
529                servo_id,
530                automaton_id,
531            } => {
532                write!(f, "Servo[{}] bind to automaton {}", servo_id, automaton_id)
533            }
534            ServoCommand::BindToParameter {
535                servo_id,
536                port,
537                parameter,
538            } => {
539                write!(f, "Servo[{}] bind to {}::{}", servo_id, port, parameter)
540            }
541            ServoCommand::Unbind { servo_id } => {
542                write!(f, "Servo[{}] unbind", servo_id)
543            }
544            ServoCommand::SetRange { servo_id, min, max } => {
545                write!(f, "Servo[{}] set range [{}, {}]", servo_id, min, max)
546            }
547            ServoCommand::SetMapping { servo_id, mapping } => {
548                write!(f, "Servo[{}] set mapping {}", servo_id, mapping)
549            }
550            ServoCommand::SetEnabled { servo_id, enabled } => {
551                write!(f, "Servo[{}] set enabled({})", servo_id, enabled)
552            }
553        }
554    }
555}
556
557impl Command for ServoCommand {}
558
559// ===== CommandType (formerly Command) — common command type =====
560
561/// Runtime command type identifier.
562#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
563pub enum CommandType {
564    /// Parameter change command.
565    SetParameter,
566    /// Automaton control command.
567    Automaton,
568    /// Sensor control command.
569    Sensor,
570    /// Servo control command.
571    Servo,
572    /// Clock tick.
573    ClockTick,
574    /// Stop command — shuts down the actor's I/O loop.
575    Stop,
576    /// System command.
577    System,
578    /// Control event from a sensor.
579    Control,
580}
581
582impl fmt::Display for CommandType {
583    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584        match self {
585            CommandType::SetParameter => write!(f, "SetParameter"),
586            CommandType::Automaton => write!(f, "Automaton"),
587            CommandType::Sensor => write!(f, "Sensor"),
588            CommandType::Servo => write!(f, "Servo"),
589            CommandType::ClockTick => write!(f, "ClockTick"),
590            CommandType::Stop => write!(f, "Stop"),
591            CommandType::System => write!(f, "System"),
592            CommandType::Control => write!(f, "Control"),
593        }
594    }
595}
596
597/// Universal command enum combining all possible command types.
598///
599/// Useful when a single queue must transport multiple command types,
600/// or when the command type is not known ahead of time.
601#[derive(Debug, Clone)]
602pub enum CommandEnum {
603    /// Parameter change command.
604    SetParameter(SetParameter),
605    /// Automaton control command.
606    Automaton(AutomatonCommand),
607    /// Sensor control command.
608    Sensor(SensorCommand),
609    /// Servo control command.
610    Servo(ServoCommand),
611    /// Clock tick — sent from Graph to Patchbay each processing block.
612    ClockTick(ClockTick),
613    /// Control event — decoded by a sensor, dispatched to a servo
614    /// for mapping to a graph parameter.
615    Control(ControlEvent),
616    /// Stop command — shuts down the actor's I/O loop.
617    Stop,
618    /// System-level command with opaque payload.
619    System {
620        /// System command kind.
621        kind: String,
622        /// Opaque command data.
623        data: Vec<u8>,
624    },
625}
626
627impl CommandEnum {
628    /// Return the runtime type tag of this command.
629    pub fn command_type(&self) -> CommandType {
630        match self {
631            CommandEnum::SetParameter(_) => CommandType::SetParameter,
632            CommandEnum::Automaton(_) => CommandType::Automaton,
633            CommandEnum::Sensor(_) => CommandType::Sensor,
634            CommandEnum::Servo(_) => CommandType::Servo,
635            CommandEnum::ClockTick(_) => CommandType::ClockTick,
636            CommandEnum::Stop => CommandType::Stop,
637            CommandEnum::System { .. } => CommandType::System,
638            CommandEnum::Control(_) => CommandType::Control,
639        }
640    }
641
642    /// If this is a `SetParameter` command, return the target `NodeId`.
643    ///
644    /// Return the timestamp if the command carries one.
645    pub fn timestamp(&self) -> Option<u64> {
646        match self {
647            CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
648            _ => None,
649        }
650    }
651
652    /// Try to downcast to `SetParameter`.
653    pub fn as_set_parameter(&self) -> Option<&SetParameter> {
654        match self {
655            CommandEnum::SetParameter(cmd) => Some(cmd),
656            _ => None,
657        }
658    }
659
660    /// Try to downcast to `AutomatonCommand`.
661    pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
662        match self {
663            CommandEnum::Automaton(cmd) => Some(cmd),
664            _ => None,
665        }
666    }
667
668    /// Try to downcast to `SensorCommand`.
669    pub fn as_sensor(&self) -> Option<&SensorCommand> {
670        match self {
671            CommandEnum::Sensor(cmd) => Some(cmd),
672            _ => None,
673        }
674    }
675
676    /// Try to downcast to `ServoCommand`.
677    pub fn as_servo(&self) -> Option<&ServoCommand> {
678        match self {
679            CommandEnum::Servo(cmd) => Some(cmd),
680            _ => None,
681        }
682    }
683
684    /// Try to downcast to `ClockTick`.
685    pub fn as_clock_tick(&self) -> Option<&ClockTick> {
686        match self {
687            CommandEnum::ClockTick(tick) => Some(tick),
688            _ => None,
689        }
690    }
691}
692
693impl fmt::Display for CommandEnum {
694    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695        match self {
696            CommandEnum::SetParameter(cmd) => write!(f, "{}", cmd),
697            CommandEnum::Automaton(cmd) => write!(f, "{}", cmd),
698            CommandEnum::Sensor(cmd) => write!(f, "{}", cmd),
699            CommandEnum::Servo(cmd) => write!(f, "{}", cmd),
700            CommandEnum::ClockTick(tick) => write!(
701                f,
702                "ClockTick(pos={}, dt={}samp)",
703                tick.sample_pos, tick.samples_since_last,
704            ),
705            CommandEnum::Stop => write!(f, "Stop"),
706            CommandEnum::System { kind, data } => {
707                write!(f, "System({kind}, {} bytes)", data.len())
708            }
709            CommandEnum::Control(event) => {
710                write!(f, "ControlEvent({event:?})")
711            }
712        }
713    }
714}
715
716// Implement the Command trait for CommandEnum (used via actor mailboxes).
717impl Command for CommandEnum {}
718
719// ===== Conversions =====
720
721/// Marker trait for types that can be converted into a command.
722pub trait ToCommand: Send + 'static {
723    /// The command type this type converts into.
724    type Command: Into<CommandEnum>;
725
726    /// Convert self into a command.
727    fn to_command(self) -> Self::Command;
728}
729
730/// Marker trait for types that can be constructed from a command.
731pub trait FromCommand: Sized {
732    /// The command type this type is constructed from.
733    type Command: TryInto<Self> + Clone;
734
735    /// Try to construct from a command.
736    fn from_command(cmd: Self::Command) -> Option<Self>;
737}
738
739impl From<SetParameter> for CommandEnum {
740    fn from(cmd: SetParameter) -> Self {
741        CommandEnum::SetParameter(cmd)
742    }
743}
744
745impl From<AutomatonCommand> for CommandEnum {
746    fn from(cmd: AutomatonCommand) -> Self {
747        CommandEnum::Automaton(cmd)
748    }
749}
750
751impl From<SensorCommand> for CommandEnum {
752    fn from(cmd: SensorCommand) -> Self {
753        CommandEnum::Sensor(cmd)
754    }
755}
756
757impl From<ServoCommand> for CommandEnum {
758    fn from(cmd: ServoCommand) -> Self {
759        CommandEnum::Servo(cmd)
760    }
761}
762
763impl TryFrom<CommandEnum> for SetParameter {
764    type Error = ();
765
766    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
767        match cmd {
768            CommandEnum::SetParameter(cmd) => Ok(cmd),
769            _ => Err(()),
770        }
771    }
772}
773
774impl TryFrom<CommandEnum> for AutomatonCommand {
775    type Error = ();
776
777    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
778        match cmd {
779            CommandEnum::Automaton(cmd) => Ok(cmd),
780            _ => Err(()),
781        }
782    }
783}
784
785impl TryFrom<CommandEnum> for SensorCommand {
786    type Error = ();
787
788    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
789        match cmd {
790            CommandEnum::Sensor(cmd) => Ok(cmd),
791            _ => Err(()),
792        }
793    }
794}
795
796impl TryFrom<CommandEnum> for ServoCommand {
797    type Error = ();
798
799    fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
800        match cmd {
801            CommandEnum::Servo(cmd) => Ok(cmd),
802            _ => Err(()),
803        }
804    }
805}