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}
114
115impl SetParameter {
116 pub fn new(
118 port: PortId,
119 parameter: ParameterId,
120 value: ParamValue,
121 source: SignalOrigin,
122 ) -> Self {
123 Self {
124 port,
125 parameter,
126 value,
127 source,
128 timestamp: Self::now(),
129 }
130 }
131
132 pub fn with_timestamp(
134 port: PortId,
135 parameter: ParameterId,
136 value: ParamValue,
137 source: SignalOrigin,
138 timestamp: u64,
139 ) -> Self {
140 Self {
141 port,
142 parameter,
143 value,
144 source,
145 timestamp,
146 }
147 }
148
149 pub fn now() -> u64 {
151 SystemTime::now()
152 .duration_since(UNIX_EPOCH)
153 .unwrap_or_default()
154 .as_micros() as u64
155 }
156}
157
158impl PartialEq for SetParameter {
159 fn eq(&self, other: &Self) -> bool {
160 self.port == other.port
161 && self.parameter == other.parameter
162 && self.value == other.value
163 && self.source == other.source
164 }
165}
166
167impl fmt::Display for SetParameter {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 write!(
170 f,
171 "[{}] {} → {}::{} = {:?}",
172 self.timestamp, self.source, self.port, self.parameter, self.value
173 )
174 }
175}
176
177impl Command for SetParameter {}
179
180#[derive(Debug, Clone)]
184pub enum AutomatonCommand {
185 SetEnabled {
187 id: String,
189 enabled: bool,
191 },
192 SetParameter {
194 id: String,
196 name: String,
198 value: f32,
200 },
201 Reset {
203 id: String,
205 },
206 Connect {
208 from: String,
210 to: String,
212 gain: f32,
214 },
215 Disconnect {
217 from: String,
219 to: String,
221 },
222 Create {
224 kind: String,
226 id: String,
228 params: Vec<(String, f32)>,
230 },
231 Destroy {
233 id: String,
235 },
236 Wake {
238 id: String,
240 },
241 UiValue {
243 id: String,
245 value: f64,
247 },
248 UiRelease {
250 id: String,
252 },
253}
254
255impl AutomatonCommand {
256 pub fn automaton_id(&self) -> Option<&str> {
258 match self {
259 AutomatonCommand::SetEnabled { id, .. } => Some(id),
260 AutomatonCommand::SetParameter { id, .. } => Some(id),
261 AutomatonCommand::Reset { id } => Some(id),
262 AutomatonCommand::Connect { from, to: _to, .. } => Some(from),
263 AutomatonCommand::Disconnect { from, to: _to } => Some(from),
264 AutomatonCommand::Create { id, .. } => Some(id),
265 AutomatonCommand::Destroy { id } => Some(id),
266 AutomatonCommand::Wake { id } => Some(id),
267 AutomatonCommand::UiValue { id, .. } => Some(id),
268 AutomatonCommand::UiRelease { id } => Some(id),
269 }
270 }
271}
272
273impl fmt::Display for AutomatonCommand {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 match self {
276 AutomatonCommand::SetEnabled { id, enabled } => {
277 write!(f, "Automaton[{}] set_enabled({})", id, enabled)
278 }
279 AutomatonCommand::SetParameter { id, name, value } => {
280 write!(f, "Automaton[{}] set_param({}={:.2})", id, name, value)
281 }
282 AutomatonCommand::Reset { id } => {
283 write!(f, "Automaton[{}] reset()", id)
284 }
285 AutomatonCommand::Connect { from, to, gain } => {
286 write!(f, "Automaton connect {} → {} gain={:.2}", from, to, gain)
287 }
288 AutomatonCommand::Disconnect { from, to } => {
289 write!(f, "Automaton disconnect {} → {}", from, to)
290 }
291 AutomatonCommand::Create { kind, id, params } => {
292 write!(
293 f,
294 "Automaton create {} as {} with {} params",
295 kind,
296 id,
297 params.len()
298 )
299 }
300 AutomatonCommand::Destroy { id } => {
301 write!(f, "Automaton destroy {}", id)
302 }
303 AutomatonCommand::Wake { id } => {
304 write!(f, "Automaton[{}] wake(tick)", id)
305 }
306 AutomatonCommand::UiValue { id, value } => {
307 write!(f, "Automaton[{}] ui_value({:.2})", id, value)
308 }
309 AutomatonCommand::UiRelease { id } => {
310 write!(f, "Automaton[{}] ui_release()", id)
311 }
312 }
313 }
314}
315
316impl Command for AutomatonCommand {}
317
318#[derive(Debug, Clone)]
322pub enum CalibrationKind {
323 Auto,
325 SetCurrentAsMin,
327 SetCurrentAsMax,
329 Reset,
331}
332
333impl fmt::Display for CalibrationKind {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 match self {
336 CalibrationKind::Auto => write!(f, "auto"),
337 CalibrationKind::SetCurrentAsMin => write!(f, "set_min"),
338 CalibrationKind::SetCurrentAsMax => write!(f, "set_max"),
339 CalibrationKind::Reset => write!(f, "reset"),
340 }
341 }
342}
343
344#[derive(Debug, Clone)]
346pub enum SensorCommand {
347 StartListening {
349 id: String,
351 source: String,
353 },
354 StopListening {
356 id: String,
358 },
359 SetSensitivity {
361 id: String,
363 value: f32,
365 },
366 Calibrate {
368 id: String,
370 kind: CalibrationKind,
372 },
373 SetEnabled {
375 id: String,
377 enabled: bool,
379 },
380}
381
382impl SensorCommand {
383 pub fn sensor_id(&self) -> &str {
385 match self {
386 SensorCommand::StartListening { id, .. } => id,
387 SensorCommand::StopListening { id } => id,
388 SensorCommand::SetSensitivity { id, .. } => id,
389 SensorCommand::Calibrate { id, .. } => id,
390 SensorCommand::SetEnabled { id, .. } => id,
391 }
392 }
393}
394
395impl fmt::Display for SensorCommand {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 match self {
398 SensorCommand::StartListening { id, source } => {
399 write!(f, "Sensor[{}] start listening to {}", id, source)
400 }
401 SensorCommand::StopListening { id } => {
402 write!(f, "Sensor[{}] stop listening", id)
403 }
404 SensorCommand::SetSensitivity { id, value } => {
405 write!(f, "Sensor[{}] set sensitivity to {:.2}", id, value)
406 }
407 SensorCommand::Calibrate { id, kind } => {
408 write!(f, "Sensor[{}] calibrate {}", id, kind)
409 }
410 SensorCommand::SetEnabled { id, enabled } => {
411 write!(f, "Sensor[{}] set enabled({})", id, enabled)
412 }
413 }
414 }
415}
416
417impl Command for SensorCommand {}
418
419#[derive(Debug, Clone)]
423pub enum MappingType {
424 Linear,
426 Exponential,
428 Logarithmic,
430 Inverted,
432 Custom(String),
434}
435
436impl fmt::Display for MappingType {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 match self {
439 MappingType::Linear => write!(f, "linear"),
440 MappingType::Exponential => write!(f, "exponential"),
441 MappingType::Logarithmic => write!(f, "logarithmic"),
442 MappingType::Inverted => write!(f, "inverted"),
443 MappingType::Custom(s) => write!(f, "custom({})", s),
444 }
445 }
446}
447
448#[derive(Debug, Clone)]
450pub enum ServoCommand {
451 BindToAutomaton {
453 servo_id: String,
455 automaton_id: String,
457 },
458 BindToParameter {
460 servo_id: String,
462 port: PortId,
464 parameter: ParameterId,
466 },
467 Unbind {
469 servo_id: String,
471 },
472 SetRange {
474 servo_id: String,
476 min: f32,
478 max: f32,
480 },
481 SetMapping {
483 servo_id: String,
485 mapping: MappingType,
487 },
488 SetEnabled {
490 servo_id: String,
492 enabled: bool,
494 },
495}
496
497impl ServoCommand {
498 pub fn servo_id(&self) -> &str {
500 match self {
501 ServoCommand::BindToAutomaton { servo_id, .. } => servo_id,
502 ServoCommand::BindToParameter { servo_id, .. } => servo_id,
503 ServoCommand::Unbind { servo_id } => servo_id,
504 ServoCommand::SetRange { servo_id, .. } => servo_id,
505 ServoCommand::SetMapping { servo_id, .. } => servo_id,
506 ServoCommand::SetEnabled { servo_id, .. } => servo_id,
507 }
508 }
509}
510
511impl fmt::Display for ServoCommand {
512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513 match self {
514 ServoCommand::BindToAutomaton {
515 servo_id,
516 automaton_id,
517 } => {
518 write!(f, "Servo[{}] bind to automaton {}", servo_id, automaton_id)
519 }
520 ServoCommand::BindToParameter {
521 servo_id,
522 port,
523 parameter,
524 } => {
525 write!(f, "Servo[{}] bind to {}::{}", servo_id, port, parameter)
526 }
527 ServoCommand::Unbind { servo_id } => {
528 write!(f, "Servo[{}] unbind", servo_id)
529 }
530 ServoCommand::SetRange { servo_id, min, max } => {
531 write!(f, "Servo[{}] set range [{}, {}]", servo_id, min, max)
532 }
533 ServoCommand::SetMapping { servo_id, mapping } => {
534 write!(f, "Servo[{}] set mapping {}", servo_id, mapping)
535 }
536 ServoCommand::SetEnabled { servo_id, enabled } => {
537 write!(f, "Servo[{}] set enabled({})", servo_id, enabled)
538 }
539 }
540 }
541}
542
543impl Command for ServoCommand {}
544
545#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
549pub enum CommandType {
550 SetParameter,
552 Automaton,
554 Sensor,
556 Servo,
558 ClockTick,
560 Stop,
562 System,
564 Control,
566}
567
568impl fmt::Display for CommandType {
569 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
570 match self {
571 CommandType::SetParameter => write!(f, "SetParameter"),
572 CommandType::Automaton => write!(f, "Automaton"),
573 CommandType::Sensor => write!(f, "Sensor"),
574 CommandType::Servo => write!(f, "Servo"),
575 CommandType::ClockTick => write!(f, "ClockTick"),
576 CommandType::Stop => write!(f, "Stop"),
577 CommandType::System => write!(f, "System"),
578 CommandType::Control => write!(f, "Control"),
579 }
580 }
581}
582
583#[derive(Debug, Clone)]
588pub enum CommandEnum {
589 SetParameter(SetParameter),
591 Automaton(AutomatonCommand),
593 Sensor(SensorCommand),
595 Servo(ServoCommand),
597 ClockTick(ClockTick),
599 Control(ControlEvent),
602 Stop,
604 System {
606 kind: String,
608 data: Vec<u8>,
610 },
611}
612
613impl CommandEnum {
614 pub fn command_type(&self) -> CommandType {
616 match self {
617 CommandEnum::SetParameter(_) => CommandType::SetParameter,
618 CommandEnum::Automaton(_) => CommandType::Automaton,
619 CommandEnum::Sensor(_) => CommandType::Sensor,
620 CommandEnum::Servo(_) => CommandType::Servo,
621 CommandEnum::ClockTick(_) => CommandType::ClockTick,
622 CommandEnum::Stop => CommandType::Stop,
623 CommandEnum::System { .. } => CommandType::System,
624 CommandEnum::Control(_) => CommandType::Control,
625 }
626 }
627
628 pub fn target_node_id(&self) -> Option<crate::traits::NodeId> {
630 match self {
631 CommandEnum::SetParameter(cmd) => Some(cmd.port.node_id()),
632 _ => None,
633 }
634 }
635
636 pub fn timestamp(&self) -> Option<u64> {
638 match self {
639 CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
640 _ => None,
641 }
642 }
643
644 pub fn as_set_parameter(&self) -> Option<&SetParameter> {
646 match self {
647 CommandEnum::SetParameter(cmd) => Some(cmd),
648 _ => None,
649 }
650 }
651
652 pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
654 match self {
655 CommandEnum::Automaton(cmd) => Some(cmd),
656 _ => None,
657 }
658 }
659
660 pub fn as_sensor(&self) -> Option<&SensorCommand> {
662 match self {
663 CommandEnum::Sensor(cmd) => Some(cmd),
664 _ => None,
665 }
666 }
667
668 pub fn as_servo(&self) -> Option<&ServoCommand> {
670 match self {
671 CommandEnum::Servo(cmd) => Some(cmd),
672 _ => None,
673 }
674 }
675
676 pub fn as_clock_tick(&self) -> Option<&ClockTick> {
678 match self {
679 CommandEnum::ClockTick(tick) => Some(tick),
680 _ => None,
681 }
682 }
683}
684
685impl fmt::Display for CommandEnum {
686 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687 match self {
688 CommandEnum::SetParameter(cmd) => write!(f, "{}", cmd),
689 CommandEnum::Automaton(cmd) => write!(f, "{}", cmd),
690 CommandEnum::Sensor(cmd) => write!(f, "{}", cmd),
691 CommandEnum::Servo(cmd) => write!(f, "{}", cmd),
692 CommandEnum::ClockTick(tick) => write!(
693 f,
694 "ClockTick(pos={}, dt={}samp)",
695 tick.sample_pos, tick.samples_since_last,
696 ),
697 CommandEnum::Stop => write!(f, "Stop"),
698 CommandEnum::System { kind, data } => {
699 write!(f, "System({kind}, {} bytes)", data.len())
700 }
701 CommandEnum::Control(event) => {
702 write!(f, "ControlEvent({event:?})")
703 }
704 }
705 }
706}
707
708impl Command for CommandEnum {}
710
711pub trait ToCommand: Send + 'static {
715 type Command: Into<CommandEnum>;
717
718 fn to_command(self) -> Self::Command;
720}
721
722pub trait FromCommand: Sized {
724 type Command: TryInto<Self> + Clone;
726
727 fn from_command(cmd: Self::Command) -> Option<Self>;
729}
730
731impl From<SetParameter> for CommandEnum {
732 fn from(cmd: SetParameter) -> Self {
733 CommandEnum::SetParameter(cmd)
734 }
735}
736
737impl From<AutomatonCommand> for CommandEnum {
738 fn from(cmd: AutomatonCommand) -> Self {
739 CommandEnum::Automaton(cmd)
740 }
741}
742
743impl From<SensorCommand> for CommandEnum {
744 fn from(cmd: SensorCommand) -> Self {
745 CommandEnum::Sensor(cmd)
746 }
747}
748
749impl From<ServoCommand> for CommandEnum {
750 fn from(cmd: ServoCommand) -> Self {
751 CommandEnum::Servo(cmd)
752 }
753}
754
755impl TryFrom<CommandEnum> for SetParameter {
756 type Error = ();
757
758 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
759 match cmd {
760 CommandEnum::SetParameter(cmd) => Ok(cmd),
761 _ => Err(()),
762 }
763 }
764}
765
766impl TryFrom<CommandEnum> for AutomatonCommand {
767 type Error = ();
768
769 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
770 match cmd {
771 CommandEnum::Automaton(cmd) => Ok(cmd),
772 _ => Err(()),
773 }
774 }
775}
776
777impl TryFrom<CommandEnum> for SensorCommand {
778 type Error = ();
779
780 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
781 match cmd {
782 CommandEnum::Sensor(cmd) => Ok(cmd),
783 _ => Err(()),
784 }
785 }
786}
787
788impl TryFrom<CommandEnum> for ServoCommand {
789 type Error = ();
790
791 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
792 match cmd {
793 CommandEnum::Servo(cmd) => Ok(cmd),
794 _ => Err(()),
795 }
796 }
797}