1use 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub enum SignalOrigin {
37 Automaton(String),
39 Sensor(String),
41 Servo(String),
43 External(String),
45 Manual,
47 Script,
49}
50
51impl SignalOrigin {
52 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 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#[derive(Debug, Clone)]
94pub struct SetParameter {
95 pub port: String,
97 pub anchor: String,
99 pub parameter: ParameterId,
101 pub value: ParamValue,
103 pub source: SignalOrigin,
105 pub timestamp: u64,
107 pub sample_pos: Option<u64>,
116}
117
118impl SetParameter {
119 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 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 pub fn with_sample_pos(mut self, sample_pos: u64) -> Self {
158 self.sample_pos = Some(sample_pos);
159 self
160 }
161
162 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
191impl Command for SetParameter {}
193
194#[derive(Debug, Clone)]
198pub enum AutomatonCommand {
199 SetEnabled {
201 id: String,
203 enabled: bool,
205 },
206 SetParameter {
208 id: String,
210 name: String,
212 value: f32,
214 },
215 Reset {
217 id: String,
219 },
220 Connect {
222 from: String,
224 to: String,
226 gain: f32,
228 },
229 Disconnect {
231 from: String,
233 to: String,
235 },
236 Create {
238 kind: String,
240 id: String,
242 params: Vec<(String, f32)>,
244 },
245 Destroy {
247 id: String,
249 },
250 Wake {
252 id: String,
254 },
255 UiValue {
257 id: String,
259 value: f64,
261 },
262 UiRelease {
264 id: String,
266 },
267}
268
269impl AutomatonCommand {
270 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#[derive(Debug, Clone)]
336pub enum CalibrationKind {
337 Auto,
339 SetCurrentAsMin,
341 SetCurrentAsMax,
343 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#[derive(Debug, Clone)]
360pub enum SensorCommand {
361 StartListening {
363 id: String,
365 source: String,
367 },
368 StopListening {
370 id: String,
372 },
373 SetSensitivity {
375 id: String,
377 value: f32,
379 },
380 Calibrate {
382 id: String,
384 kind: CalibrationKind,
386 },
387 SetEnabled {
389 id: String,
391 enabled: bool,
393 },
394}
395
396impl SensorCommand {
397 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#[derive(Debug, Clone)]
437pub enum MappingType {
438 Linear,
440 Exponential,
442 Logarithmic,
444 Inverted,
446 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#[derive(Debug, Clone)]
464pub enum ServoCommand {
465 BindToAutomaton {
467 servo_id: String,
469 automaton_id: String,
471 },
472 BindToParameter {
474 servo_id: String,
476 port: String,
478 parameter: ParameterId,
480 },
481 Unbind {
483 servo_id: String,
485 },
486 SetRange {
488 servo_id: String,
490 min: f32,
492 max: f32,
494 },
495 SetMapping {
497 servo_id: String,
499 mapping: MappingType,
501 },
502 SetEnabled {
504 servo_id: String,
506 enabled: bool,
508 },
509}
510
511impl ServoCommand {
512 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
563pub enum CommandType {
564 SetParameter,
566 Automaton,
568 Sensor,
570 Servo,
572 ClockTick,
574 Stop,
576 System,
578 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#[derive(Debug, Clone)]
602pub enum CommandEnum {
603 SetParameter(SetParameter),
605 Automaton(AutomatonCommand),
607 Sensor(SensorCommand),
609 Servo(ServoCommand),
611 ClockTick(ClockTick),
613 Control(ControlEvent),
616 Stop,
618 System {
620 kind: String,
622 data: Vec<u8>,
624 },
625}
626
627impl CommandEnum {
628 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 pub fn timestamp(&self) -> Option<u64> {
646 match self {
647 CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
648 _ => None,
649 }
650 }
651
652 pub fn as_set_parameter(&self) -> Option<&SetParameter> {
654 match self {
655 CommandEnum::SetParameter(cmd) => Some(cmd),
656 _ => None,
657 }
658 }
659
660 pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
662 match self {
663 CommandEnum::Automaton(cmd) => Some(cmd),
664 _ => None,
665 }
666 }
667
668 pub fn as_sensor(&self) -> Option<&SensorCommand> {
670 match self {
671 CommandEnum::Sensor(cmd) => Some(cmd),
672 _ => None,
673 }
674 }
675
676 pub fn as_servo(&self) -> Option<&ServoCommand> {
678 match self {
679 CommandEnum::Servo(cmd) => Some(cmd),
680 _ => None,
681 }
682 }
683
684 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
716impl Command for CommandEnum {}
718
719pub trait ToCommand: Send + 'static {
723 type Command: Into<CommandEnum>;
725
726 fn to_command(self) -> Self::Command;
728}
729
730pub trait FromCommand: Sized {
732 type Command: TryInto<Self> + Clone;
734
735 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}