1use 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub enum SignalOrigin {
45 Automaton(String),
47 Sensor(String),
49 Servo(String),
51 External(String),
53 Manual,
55 Script,
57}
58
59impl SignalOrigin {
60 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 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#[derive(Debug, Clone)]
102pub struct SetParameter {
103 pub port: PortId,
105 pub parameter: ParameterId,
107 pub value: ParamValue,
109 pub source: SignalOrigin,
111 pub timestamp: u64,
113 pub sample_pos: Option<u64>,
122}
123
124impl SetParameter {
125 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 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 pub fn with_sample_pos(mut self, sample_pos: u64) -> Self {
162 self.sample_pos = Some(sample_pos);
163 self
164 }
165
166 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
194impl Command for SetParameter {}
196
197#[derive(Debug, Clone)]
201pub enum AutomatonCommand {
202 SetEnabled {
204 id: String,
206 enabled: bool,
208 },
209 SetParameter {
211 id: String,
213 name: String,
215 value: f32,
217 },
218 Reset {
220 id: String,
222 },
223 Connect {
225 from: String,
227 to: String,
229 gain: f32,
231 },
232 Disconnect {
234 from: String,
236 to: String,
238 },
239 Create {
241 kind: String,
243 id: String,
245 params: Vec<(String, f32)>,
247 },
248 Destroy {
250 id: String,
252 },
253 Wake {
255 id: String,
257 },
258 UiValue {
260 id: String,
262 value: f64,
264 },
265 UiRelease {
267 id: String,
269 },
270}
271
272impl AutomatonCommand {
273 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#[derive(Debug, Clone)]
339pub enum CalibrationKind {
340 Auto,
342 SetCurrentAsMin,
344 SetCurrentAsMax,
346 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#[derive(Debug, Clone)]
363pub enum SensorCommand {
364 StartListening {
366 id: String,
368 source: String,
370 },
371 StopListening {
373 id: String,
375 },
376 SetSensitivity {
378 id: String,
380 value: f32,
382 },
383 Calibrate {
385 id: String,
387 kind: CalibrationKind,
389 },
390 SetEnabled {
392 id: String,
394 enabled: bool,
396 },
397}
398
399impl SensorCommand {
400 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#[derive(Debug, Clone)]
440pub enum MappingType {
441 Linear,
443 Exponential,
445 Logarithmic,
447 Inverted,
449 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#[derive(Debug, Clone)]
467pub enum ServoCommand {
468 BindToAutomaton {
470 servo_id: String,
472 automaton_id: String,
474 },
475 BindToParameter {
477 servo_id: String,
479 port: PortId,
481 parameter: ParameterId,
483 },
484 Unbind {
486 servo_id: String,
488 },
489 SetRange {
491 servo_id: String,
493 min: f32,
495 max: f32,
497 },
498 SetMapping {
500 servo_id: String,
502 mapping: MappingType,
504 },
505 SetEnabled {
507 servo_id: String,
509 enabled: bool,
511 },
512}
513
514impl ServoCommand {
515 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
566pub enum CommandType {
567 SetParameter,
569 Automaton,
571 Sensor,
573 Servo,
575 ClockTick,
577 Stop,
579 System,
581 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#[derive(Debug, Clone)]
605pub enum CommandEnum {
606 SetParameter(SetParameter),
608 Automaton(AutomatonCommand),
610 Sensor(SensorCommand),
612 Servo(ServoCommand),
614 ClockTick(ClockTick),
616 Control(ControlEvent),
619 Stop,
621 System {
623 kind: String,
625 data: Vec<u8>,
627 },
628}
629
630impl CommandEnum {
631 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 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 pub fn timestamp(&self) -> Option<u64> {
655 match self {
656 CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
657 _ => None,
658 }
659 }
660
661 pub fn as_set_parameter(&self) -> Option<&SetParameter> {
663 match self {
664 CommandEnum::SetParameter(cmd) => Some(cmd),
665 _ => None,
666 }
667 }
668
669 pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
671 match self {
672 CommandEnum::Automaton(cmd) => Some(cmd),
673 _ => None,
674 }
675 }
676
677 pub fn as_sensor(&self) -> Option<&SensorCommand> {
679 match self {
680 CommandEnum::Sensor(cmd) => Some(cmd),
681 _ => None,
682 }
683 }
684
685 pub fn as_servo(&self) -> Option<&ServoCommand> {
687 match self {
688 CommandEnum::Servo(cmd) => Some(cmd),
689 _ => None,
690 }
691 }
692
693 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
725impl Command for CommandEnum {}
727
728pub trait ToCommand: Send + 'static {
732 type Command: Into<CommandEnum>;
734
735 fn to_command(self) -> Self::Command;
737}
738
739pub trait FromCommand: Sized {
741 type Command: TryInto<Self> + Clone;
743
744 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}