1use super::command::Command;
29use crate::time::ClockTick;
30use crate::traits::{ParamValue, ParameterId, PortId};
31use std::fmt;
32use std::time::{SystemTime, UNIX_EPOCH};
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
43pub enum SignalOrigin {
44 Automaton(String),
46 Sensor(String),
48 Servo(String),
50 External(String),
52 Manual,
54 Script,
56}
57
58impl SignalOrigin {
59 pub fn name(&self) -> &str {
61 match self {
62 SignalOrigin::Automaton(name) => name,
63 SignalOrigin::Sensor(name) => name,
64 SignalOrigin::Servo(name) => name,
65 SignalOrigin::External(name) => name,
66 SignalOrigin::Manual => "manual",
67 SignalOrigin::Script => "script",
68 }
69 }
70
71 pub fn kind(&self) -> &'static str {
73 match self {
74 SignalOrigin::Automaton(_) => "automaton",
75 SignalOrigin::Sensor(_) => "sensor",
76 SignalOrigin::Servo(_) => "servo",
77 SignalOrigin::External(_) => "external",
78 SignalOrigin::Manual => "manual",
79 SignalOrigin::Script => "script",
80 }
81 }
82}
83
84impl fmt::Display for SignalOrigin {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 SignalOrigin::Automaton(name) => write!(f, "⚙️ {}", name),
88 SignalOrigin::Sensor(name) => write!(f, "👁️ {}", name),
89 SignalOrigin::Servo(name) => write!(f, "🦾 {}", name),
90 SignalOrigin::External(name) => write!(f, "🌍 {}", name),
91 SignalOrigin::Manual => write!(f, "👤 manual"),
92 SignalOrigin::Script => write!(f, "📜 script"),
93 }
94 }
95}
96
97#[derive(Debug, Clone)]
101pub struct SetParameter {
102 pub port: PortId,
104 pub parameter: ParameterId,
106 pub value: ParamValue,
108 pub source: SignalOrigin,
110 pub timestamp: u64,
112}
113
114impl SetParameter {
115 pub fn new(
117 port: PortId,
118 parameter: ParameterId,
119 value: ParamValue,
120 source: SignalOrigin,
121 ) -> Self {
122 Self {
123 port,
124 parameter,
125 value,
126 source,
127 timestamp: Self::now(),
128 }
129 }
130
131 pub fn with_timestamp(
133 port: PortId,
134 parameter: ParameterId,
135 value: ParamValue,
136 source: SignalOrigin,
137 timestamp: u64,
138 ) -> Self {
139 Self {
140 port,
141 parameter,
142 value,
143 source,
144 timestamp,
145 }
146 }
147
148 pub fn now() -> u64 {
150 SystemTime::now()
151 .duration_since(UNIX_EPOCH)
152 .unwrap_or_default()
153 .as_micros() as u64
154 }
155}
156
157impl PartialEq for SetParameter {
158 fn eq(&self, other: &Self) -> bool {
159 self.port == other.port
160 && self.parameter == other.parameter
161 && self.value == other.value
162 && self.source == other.source
163 }
164}
165
166impl fmt::Display for SetParameter {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 write!(
169 f,
170 "[{}] {} → {}::{} = {:?}",
171 self.timestamp, self.source, self.port, self.parameter, self.value
172 )
173 }
174}
175
176impl Command for SetParameter {}
178
179#[derive(Debug, Clone)]
183pub enum AutomatonCommand {
184 SetEnabled {
186 id: String,
188 enabled: bool,
190 },
191 SetParameter {
193 id: String,
195 name: String,
197 value: f32,
199 },
200 Reset {
202 id: String,
204 },
205 Connect {
207 from: String,
209 to: String,
211 gain: f32,
213 },
214 Disconnect {
216 from: String,
218 to: String,
220 },
221 Create {
223 kind: String,
225 id: String,
227 params: Vec<(String, f32)>,
229 },
230 Destroy {
232 id: String,
234 },
235 Wake {
237 id: String,
239 },
240 UiValue {
242 id: String,
244 value: f64,
246 },
247 UiRelease {
249 id: String,
251 },
252}
253
254impl AutomatonCommand {
255 pub fn automaton_id(&self) -> Option<&str> {
257 match self {
258 AutomatonCommand::SetEnabled { id, .. } => Some(id),
259 AutomatonCommand::SetParameter { id, .. } => Some(id),
260 AutomatonCommand::Reset { id } => Some(id),
261 AutomatonCommand::Connect { from, to: _to, .. } => Some(from),
262 AutomatonCommand::Disconnect { from, to: _to } => Some(from),
263 AutomatonCommand::Create { id, .. } => Some(id),
264 AutomatonCommand::Destroy { id } => Some(id),
265 AutomatonCommand::Wake { id } => Some(id),
266 AutomatonCommand::UiValue { id, .. } => Some(id),
267 AutomatonCommand::UiRelease { id } => Some(id),
268 }
269 }
270}
271
272impl fmt::Display for AutomatonCommand {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 match self {
275 AutomatonCommand::SetEnabled { id, enabled } => {
276 write!(f, "Automaton[{}] set_enabled({})", id, enabled)
277 }
278 AutomatonCommand::SetParameter { id, name, value } => {
279 write!(f, "Automaton[{}] set_param({}={:.2})", id, name, value)
280 }
281 AutomatonCommand::Reset { id } => {
282 write!(f, "Automaton[{}] reset()", id)
283 }
284 AutomatonCommand::Connect { from, to, gain } => {
285 write!(f, "Automaton connect {} → {} gain={:.2}", from, to, gain)
286 }
287 AutomatonCommand::Disconnect { from, to } => {
288 write!(f, "Automaton disconnect {} → {}", from, to)
289 }
290 AutomatonCommand::Create { kind, id, params } => {
291 write!(
292 f,
293 "Automaton create {} as {} with {} params",
294 kind,
295 id,
296 params.len()
297 )
298 }
299 AutomatonCommand::Destroy { id } => {
300 write!(f, "Automaton destroy {}", id)
301 }
302 AutomatonCommand::Wake { id } => {
303 write!(f, "Automaton[{}] wake(tick)", id)
304 }
305 AutomatonCommand::UiValue { id, value } => {
306 write!(f, "Automaton[{}] ui_value({:.2})", id, value)
307 }
308 AutomatonCommand::UiRelease { id } => {
309 write!(f, "Automaton[{}] ui_release()", id)
310 }
311 }
312 }
313}
314
315impl Command for AutomatonCommand {}
316
317#[derive(Debug, Clone)]
321pub enum CalibrationKind {
322 Auto,
324 SetCurrentAsMin,
326 SetCurrentAsMax,
328 Reset,
330}
331
332impl fmt::Display for CalibrationKind {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 match self {
335 CalibrationKind::Auto => write!(f, "auto"),
336 CalibrationKind::SetCurrentAsMin => write!(f, "set_min"),
337 CalibrationKind::SetCurrentAsMax => write!(f, "set_max"),
338 CalibrationKind::Reset => write!(f, "reset"),
339 }
340 }
341}
342
343#[derive(Debug, Clone)]
345pub enum SensorCommand {
346 StartListening {
348 id: String,
350 source: String,
352 },
353 StopListening {
355 id: String,
357 },
358 SetSensitivity {
360 id: String,
362 value: f32,
364 },
365 Calibrate {
367 id: String,
369 kind: CalibrationKind,
371 },
372 SetEnabled {
374 id: String,
376 enabled: bool,
378 },
379}
380
381impl SensorCommand {
382 pub fn sensor_id(&self) -> &str {
384 match self {
385 SensorCommand::StartListening { id, .. } => id,
386 SensorCommand::StopListening { id } => id,
387 SensorCommand::SetSensitivity { id, .. } => id,
388 SensorCommand::Calibrate { id, .. } => id,
389 SensorCommand::SetEnabled { id, .. } => id,
390 }
391 }
392}
393
394impl fmt::Display for SensorCommand {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 match self {
397 SensorCommand::StartListening { id, source } => {
398 write!(f, "Sensor[{}] start listening to {}", id, source)
399 }
400 SensorCommand::StopListening { id } => {
401 write!(f, "Sensor[{}] stop listening", id)
402 }
403 SensorCommand::SetSensitivity { id, value } => {
404 write!(f, "Sensor[{}] set sensitivity to {:.2}", id, value)
405 }
406 SensorCommand::Calibrate { id, kind } => {
407 write!(f, "Sensor[{}] calibrate {}", id, kind)
408 }
409 SensorCommand::SetEnabled { id, enabled } => {
410 write!(f, "Sensor[{}] set enabled({})", id, enabled)
411 }
412 }
413 }
414}
415
416impl Command for SensorCommand {}
417
418#[derive(Debug, Clone)]
422pub enum MappingType {
423 Linear,
425 Exponential,
427 Logarithmic,
429 Inverted,
431 Custom(String),
433}
434
435impl fmt::Display for MappingType {
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 match self {
438 MappingType::Linear => write!(f, "linear"),
439 MappingType::Exponential => write!(f, "exponential"),
440 MappingType::Logarithmic => write!(f, "logarithmic"),
441 MappingType::Inverted => write!(f, "inverted"),
442 MappingType::Custom(s) => write!(f, "custom({})", s),
443 }
444 }
445}
446
447#[derive(Debug, Clone)]
449pub enum ServoCommand {
450 BindToAutomaton {
452 servo_id: String,
454 automaton_id: String,
456 },
457 BindToParameter {
459 servo_id: String,
461 port: PortId,
463 parameter: ParameterId,
465 },
466 Unbind {
468 servo_id: String,
470 },
471 SetRange {
473 servo_id: String,
475 min: f32,
477 max: f32,
479 },
480 SetMapping {
482 servo_id: String,
484 mapping: MappingType,
486 },
487 SetEnabled {
489 servo_id: String,
491 enabled: bool,
493 },
494}
495
496impl ServoCommand {
497 pub fn servo_id(&self) -> &str {
499 match self {
500 ServoCommand::BindToAutomaton { servo_id, .. } => servo_id,
501 ServoCommand::BindToParameter { servo_id, .. } => servo_id,
502 ServoCommand::Unbind { servo_id } => servo_id,
503 ServoCommand::SetRange { servo_id, .. } => servo_id,
504 ServoCommand::SetMapping { servo_id, .. } => servo_id,
505 ServoCommand::SetEnabled { servo_id, .. } => servo_id,
506 }
507 }
508}
509
510impl fmt::Display for ServoCommand {
511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512 match self {
513 ServoCommand::BindToAutomaton {
514 servo_id,
515 automaton_id,
516 } => {
517 write!(f, "Servo[{}] bind to automaton {}", servo_id, automaton_id)
518 }
519 ServoCommand::BindToParameter {
520 servo_id,
521 port,
522 parameter,
523 } => {
524 write!(f, "Servo[{}] bind to {}::{}", servo_id, port, parameter)
525 }
526 ServoCommand::Unbind { servo_id } => {
527 write!(f, "Servo[{}] unbind", servo_id)
528 }
529 ServoCommand::SetRange { servo_id, min, max } => {
530 write!(f, "Servo[{}] set range [{}, {}]", servo_id, min, max)
531 }
532 ServoCommand::SetMapping { servo_id, mapping } => {
533 write!(f, "Servo[{}] set mapping {}", servo_id, mapping)
534 }
535 ServoCommand::SetEnabled { servo_id, enabled } => {
536 write!(f, "Servo[{}] set enabled({})", servo_id, enabled)
537 }
538 }
539 }
540}
541
542impl Command for ServoCommand {}
543
544#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
548pub enum CommandType {
549 SetParameter,
551 Automaton,
553 Sensor,
555 Servo,
557 ClockTick,
559 Stop,
561 System,
563}
564
565impl fmt::Display for CommandType {
566 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567 match self {
568 CommandType::SetParameter => write!(f, "SetParameter"),
569 CommandType::Automaton => write!(f, "Automaton"),
570 CommandType::Sensor => write!(f, "Sensor"),
571 CommandType::Servo => write!(f, "Servo"),
572 CommandType::ClockTick => write!(f, "ClockTick"),
573 CommandType::Stop => write!(f, "Stop"),
574 CommandType::System => write!(f, "System"),
575 }
576 }
577}
578
579#[derive(Debug, Clone)]
584pub enum CommandEnum {
585 SetParameter(SetParameter),
587 Automaton(AutomatonCommand),
589 Sensor(SensorCommand),
591 Servo(ServoCommand),
593 ClockTick(ClockTick),
595 Stop,
597 System {
599 kind: String,
601 data: Vec<u8>,
603 },
604}
605
606impl CommandEnum {
607 pub fn command_type(&self) -> CommandType {
609 match self {
610 CommandEnum::SetParameter(_) => CommandType::SetParameter,
611 CommandEnum::Automaton(_) => CommandType::Automaton,
612 CommandEnum::Sensor(_) => CommandType::Sensor,
613 CommandEnum::Servo(_) => CommandType::Servo,
614 CommandEnum::ClockTick(_) => CommandType::ClockTick,
615 CommandEnum::Stop => CommandType::Stop,
616 CommandEnum::System { .. } => CommandType::System,
617 }
618 }
619
620 pub fn target_node_id(&self) -> Option<crate::traits::NodeId> {
622 match self {
623 CommandEnum::SetParameter(cmd) => Some(cmd.port.node_id()),
624 _ => None,
625 }
626 }
627
628 pub fn timestamp(&self) -> Option<u64> {
630 match self {
631 CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
632 _ => None,
633 }
634 }
635
636 pub fn as_set_parameter(&self) -> Option<&SetParameter> {
638 match self {
639 CommandEnum::SetParameter(cmd) => Some(cmd),
640 _ => None,
641 }
642 }
643
644 pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
646 match self {
647 CommandEnum::Automaton(cmd) => Some(cmd),
648 _ => None,
649 }
650 }
651
652 pub fn as_sensor(&self) -> Option<&SensorCommand> {
654 match self {
655 CommandEnum::Sensor(cmd) => Some(cmd),
656 _ => None,
657 }
658 }
659
660 pub fn as_servo(&self) -> Option<&ServoCommand> {
662 match self {
663 CommandEnum::Servo(cmd) => Some(cmd),
664 _ => None,
665 }
666 }
667
668 pub fn as_clock_tick(&self) -> Option<&ClockTick> {
670 match self {
671 CommandEnum::ClockTick(tick) => Some(tick),
672 _ => None,
673 }
674 }
675}
676
677impl fmt::Display for CommandEnum {
678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679 match self {
680 CommandEnum::SetParameter(cmd) => write!(f, "{}", cmd),
681 CommandEnum::Automaton(cmd) => write!(f, "{}", cmd),
682 CommandEnum::Sensor(cmd) => write!(f, "{}", cmd),
683 CommandEnum::Servo(cmd) => write!(f, "{}", cmd),
684 CommandEnum::ClockTick(tick) => write!(
685 f,
686 "ClockTick(pos={}, dt={}samp)",
687 tick.sample_pos, tick.samples_since_last,
688 ),
689 CommandEnum::Stop => write!(f, "Stop"),
690 CommandEnum::System { kind, data } => {
691 write!(f, "System[{}] ({} bytes)", kind, data.len())
692 }
693 }
694 }
695}
696
697impl Command for CommandEnum {}
699
700pub trait ToCommand: Send + 'static {
704 type Command: Into<CommandEnum>;
706
707 fn to_command(self) -> Self::Command;
709}
710
711pub trait FromCommand: Sized {
713 type Command: TryInto<Self> + Clone;
715
716 fn from_command(cmd: Self::Command) -> Option<Self>;
718}
719
720impl From<SetParameter> for CommandEnum {
721 fn from(cmd: SetParameter) -> Self {
722 CommandEnum::SetParameter(cmd)
723 }
724}
725
726impl From<AutomatonCommand> for CommandEnum {
727 fn from(cmd: AutomatonCommand) -> Self {
728 CommandEnum::Automaton(cmd)
729 }
730}
731
732impl From<SensorCommand> for CommandEnum {
733 fn from(cmd: SensorCommand) -> Self {
734 CommandEnum::Sensor(cmd)
735 }
736}
737
738impl From<ServoCommand> for CommandEnum {
739 fn from(cmd: ServoCommand) -> Self {
740 CommandEnum::Servo(cmd)
741 }
742}
743
744impl TryFrom<CommandEnum> for SetParameter {
745 type Error = ();
746
747 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
748 match cmd {
749 CommandEnum::SetParameter(cmd) => Ok(cmd),
750 _ => Err(()),
751 }
752 }
753}
754
755impl TryFrom<CommandEnum> for AutomatonCommand {
756 type Error = ();
757
758 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
759 match cmd {
760 CommandEnum::Automaton(cmd) => Ok(cmd),
761 _ => Err(()),
762 }
763 }
764}
765
766impl TryFrom<CommandEnum> for SensorCommand {
767 type Error = ();
768
769 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
770 match cmd {
771 CommandEnum::Sensor(cmd) => Ok(cmd),
772 _ => Err(()),
773 }
774 }
775}
776
777impl TryFrom<CommandEnum> for ServoCommand {
778 type Error = ();
779
780 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
781 match cmd {
782 CommandEnum::Servo(cmd) => Ok(cmd),
783 _ => Err(()),
784 }
785 }
786}