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