1use super::command::Command;
43use crate::traits::{ParameterId, PortId};
44use std::fmt;
45use std::time::{SystemTime, UNIX_EPOCH};
46
47#[derive(Debug, Clone, PartialEq, Eq, Hash)]
56pub enum SignalSource {
57 Automaton(String),
59 Sensor(String),
61 Servo(String),
63 External(String),
65 Manual,
67 Script,
69}
70
71impl SignalSource {
72 pub fn name(&self) -> &str {
74 match self {
75 SignalSource::Automaton(name) => name,
76 SignalSource::Sensor(name) => name,
77 SignalSource::Servo(name) => name,
78 SignalSource::External(name) => name,
79 SignalSource::Manual => "manual",
80 SignalSource::Script => "script",
81 }
82 }
83
84 pub fn kind(&self) -> &'static str {
86 match self {
87 SignalSource::Automaton(_) => "automaton",
88 SignalSource::Sensor(_) => "sensor",
89 SignalSource::Servo(_) => "servo",
90 SignalSource::External(_) => "external",
91 SignalSource::Manual => "manual",
92 SignalSource::Script => "script",
93 }
94 }
95}
96
97impl fmt::Display for SignalSource {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 SignalSource::Automaton(name) => write!(f, "⚙️ {}", name),
101 SignalSource::Sensor(name) => write!(f, "👁️ {}", name),
102 SignalSource::Servo(name) => write!(f, "🦾 {}", name),
103 SignalSource::External(name) => write!(f, "🌍 {}", name),
104 SignalSource::Manual => write!(f, "👤 manual"),
105 SignalSource::Script => write!(f, "📜 script"),
106 }
107 }
108}
109
110#[derive(Debug, Clone)]
114pub struct SetParameter {
115 pub port: PortId,
117 pub parameter: ParameterId,
119 pub value: f32,
121 pub source: SignalSource,
123 pub timestamp: u64,
125}
126
127impl SetParameter {
128 pub fn new(port: PortId, parameter: ParameterId, value: f32, source: SignalSource) -> Self {
130 Self {
131 port,
132 parameter,
133 value,
134 source,
135 timestamp: Self::now(),
136 }
137 }
138
139 pub fn with_timestamp(
141 port: PortId,
142 parameter: ParameterId,
143 value: f32,
144 source: SignalSource,
145 timestamp: u64,
146 ) -> Self {
147 Self {
148 port,
149 parameter,
150 value,
151 source,
152 timestamp,
153 }
154 }
155
156 pub fn now() -> u64 {
158 SystemTime::now()
159 .duration_since(UNIX_EPOCH)
160 .unwrap_or_default()
161 .as_micros() as u64
162 }
163}
164
165impl PartialEq for SetParameter {
166 fn eq(&self, other: &Self) -> bool {
167 self.port == other.port
168 && self.parameter == other.parameter
169 && (self.value - other.value).abs() < f32::EPSILON
170 && self.source == other.source
171 }
172}
173
174impl fmt::Display for SetParameter {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(
177 f,
178 "[{}] {} → {}::{} = {:.3}",
179 self.timestamp, self.source, self.port, self.parameter, self.value
180 )
181 }
182}
183
184impl Command for SetParameter {}
186
187#[derive(Debug, Clone)]
191pub enum AutomatonCommand {
192 SetEnabled {
194 id: String,
196 enabled: bool,
198 },
199 SetParameter {
201 id: String,
203 name: String,
205 value: f32,
207 },
208 Reset {
210 id: String,
212 },
213 Connect {
215 from: String,
217 to: String,
219 gain: f32,
221 },
222 Disconnect {
224 from: String,
226 to: String,
228 },
229 Create {
231 kind: String,
233 id: String,
235 params: Vec<(String, f32)>,
237 },
238 Destroy {
240 id: String,
242 },
243}
244
245impl AutomatonCommand {
246 pub fn automaton_id(&self) -> Option<&str> {
248 match self {
249 AutomatonCommand::SetEnabled { id, .. } => Some(id),
250 AutomatonCommand::SetParameter { id, .. } => Some(id),
251 AutomatonCommand::Reset { id } => Some(id),
252 AutomatonCommand::Connect { from, to: _to, .. } => Some(from),
253 AutomatonCommand::Disconnect { from, to: _to } => Some(from),
254 AutomatonCommand::Create { id, .. } => Some(id),
255 AutomatonCommand::Destroy { id } => Some(id),
256 }
257 }
258}
259
260impl fmt::Display for AutomatonCommand {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 match self {
263 AutomatonCommand::SetEnabled { id, enabled } => {
264 write!(f, "Automaton[{}] set_enabled({})", id, enabled)
265 }
266 AutomatonCommand::SetParameter { id, name, value } => {
267 write!(f, "Automaton[{}] set_param({}={:.2})", id, name, value)
268 }
269 AutomatonCommand::Reset { id } => {
270 write!(f, "Automaton[{}] reset()", id)
271 }
272 AutomatonCommand::Connect { from, to, gain } => {
273 write!(f, "Automaton connect {} → {} gain={:.2}", from, to, gain)
274 }
275 AutomatonCommand::Disconnect { from, to } => {
276 write!(f, "Automaton disconnect {} → {}", from, to)
277 }
278 AutomatonCommand::Create { kind, id, params } => {
279 write!(
280 f,
281 "Automaton create {} as {} with {} params",
282 kind,
283 id,
284 params.len()
285 )
286 }
287 AutomatonCommand::Destroy { id } => {
288 write!(f, "Automaton destroy {}", id)
289 }
290 }
291 }
292}
293
294impl Command for AutomatonCommand {}
295
296#[derive(Debug, Clone)]
300pub enum CalibrationKind {
301 Auto,
303 SetCurrentAsMin,
305 SetCurrentAsMax,
307 Reset,
309}
310
311impl fmt::Display for CalibrationKind {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 match self {
314 CalibrationKind::Auto => write!(f, "auto"),
315 CalibrationKind::SetCurrentAsMin => write!(f, "set_min"),
316 CalibrationKind::SetCurrentAsMax => write!(f, "set_max"),
317 CalibrationKind::Reset => write!(f, "reset"),
318 }
319 }
320}
321
322#[derive(Debug, Clone)]
324pub enum SensorCommand {
325 StartListening {
327 id: String,
329 source: String,
331 },
332 StopListening {
334 id: String,
336 },
337 SetSensitivity {
339 id: String,
341 value: f32,
343 },
344 Calibrate {
346 id: String,
348 kind: CalibrationKind,
350 },
351 SetEnabled {
353 id: String,
355 enabled: bool,
357 },
358}
359
360impl SensorCommand {
361 pub fn sensor_id(&self) -> &str {
363 match self {
364 SensorCommand::StartListening { id, .. } => id,
365 SensorCommand::StopListening { id } => id,
366 SensorCommand::SetSensitivity { id, .. } => id,
367 SensorCommand::Calibrate { id, .. } => id,
368 SensorCommand::SetEnabled { id, .. } => id,
369 }
370 }
371}
372
373impl fmt::Display for SensorCommand {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 match self {
376 SensorCommand::StartListening { id, source } => {
377 write!(f, "Sensor[{}] start listening to {}", id, source)
378 }
379 SensorCommand::StopListening { id } => {
380 write!(f, "Sensor[{}] stop listening", id)
381 }
382 SensorCommand::SetSensitivity { id, value } => {
383 write!(f, "Sensor[{}] set sensitivity to {:.2}", id, value)
384 }
385 SensorCommand::Calibrate { id, kind } => {
386 write!(f, "Sensor[{}] calibrate {}", id, kind)
387 }
388 SensorCommand::SetEnabled { id, enabled } => {
389 write!(f, "Sensor[{}] set enabled({})", id, enabled)
390 }
391 }
392 }
393}
394
395impl Command for SensorCommand {}
396
397#[derive(Debug, Clone)]
401pub enum MappingType {
402 Linear,
404 Exponential,
406 Logarithmic,
408 Inverted,
410 Custom(String),
412}
413
414impl fmt::Display for MappingType {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416 match self {
417 MappingType::Linear => write!(f, "linear"),
418 MappingType::Exponential => write!(f, "exponential"),
419 MappingType::Logarithmic => write!(f, "logarithmic"),
420 MappingType::Inverted => write!(f, "inverted"),
421 MappingType::Custom(s) => write!(f, "custom({})", s),
422 }
423 }
424}
425
426#[derive(Debug, Clone)]
428pub enum ServoCommand {
429 BindToAutomaton {
431 servo_id: String,
433 automaton_id: String,
435 },
436 BindToParameter {
438 servo_id: String,
440 port: PortId,
442 parameter: ParameterId,
444 },
445 Unbind {
447 servo_id: String,
449 },
450 SetRange {
452 servo_id: String,
454 min: f32,
456 max: f32,
458 },
459 SetMapping {
461 servo_id: String,
463 mapping: MappingType,
465 },
466 SetEnabled {
468 servo_id: String,
470 enabled: bool,
472 },
473}
474
475impl ServoCommand {
476 pub fn servo_id(&self) -> &str {
478 match self {
479 ServoCommand::BindToAutomaton { servo_id, .. } => servo_id,
480 ServoCommand::BindToParameter { servo_id, .. } => servo_id,
481 ServoCommand::Unbind { servo_id } => servo_id,
482 ServoCommand::SetRange { servo_id, .. } => servo_id,
483 ServoCommand::SetMapping { servo_id, .. } => servo_id,
484 ServoCommand::SetEnabled { servo_id, .. } => servo_id,
485 }
486 }
487}
488
489impl fmt::Display for ServoCommand {
490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491 match self {
492 ServoCommand::BindToAutomaton {
493 servo_id,
494 automaton_id,
495 } => {
496 write!(f, "Servo[{}] bind to automaton {}", servo_id, automaton_id)
497 }
498 ServoCommand::BindToParameter {
499 servo_id,
500 port,
501 parameter,
502 } => {
503 write!(f, "Servo[{}] bind to {}::{}", servo_id, port, parameter)
504 }
505 ServoCommand::Unbind { servo_id } => {
506 write!(f, "Servo[{}] unbind", servo_id)
507 }
508 ServoCommand::SetRange { servo_id, min, max } => {
509 write!(f, "Servo[{}] set range [{}, {}]", servo_id, min, max)
510 }
511 ServoCommand::SetMapping { servo_id, mapping } => {
512 write!(f, "Servo[{}] set mapping {}", servo_id, mapping)
513 }
514 ServoCommand::SetEnabled { servo_id, enabled } => {
515 write!(f, "Servo[{}] set enabled({})", servo_id, enabled)
516 }
517 }
518 }
519}
520
521impl Command for ServoCommand {}
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
527pub enum CommandType {
528 SetParameter,
530 Automaton,
532 Sensor,
534 Servo,
536 System,
538}
539
540impl fmt::Display for CommandType {
541 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542 match self {
543 CommandType::SetParameter => write!(f, "SetParameter"),
544 CommandType::Automaton => write!(f, "Automaton"),
545 CommandType::Sensor => write!(f, "Sensor"),
546 CommandType::Servo => write!(f, "Servo"),
547 CommandType::System => write!(f, "System"),
548 }
549 }
550}
551
552#[derive(Debug, Clone)]
557pub enum CommandEnum {
558 SetParameter(SetParameter),
560 Automaton(AutomatonCommand),
562 Sensor(SensorCommand),
564 Servo(ServoCommand),
566 System {
568 kind: String,
570 data: Vec<u8>,
572 },
573}
574
575impl CommandEnum {
576 pub fn command_type(&self) -> CommandType {
578 match self {
579 CommandEnum::SetParameter(_) => CommandType::SetParameter,
580 CommandEnum::Automaton(_) => CommandType::Automaton,
581 CommandEnum::Sensor(_) => CommandType::Sensor,
582 CommandEnum::Servo(_) => CommandType::Servo,
583 CommandEnum::System { .. } => CommandType::System,
584 }
585 }
586
587 pub fn target_node_id(&self) -> Option<crate::traits::NodeId> {
589 match self {
590 CommandEnum::SetParameter(cmd) => Some(cmd.port.node_id()),
591 _ => None,
592 }
593 }
594
595 pub fn timestamp(&self) -> Option<u64> {
597 match self {
598 CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
599 _ => None,
600 }
601 }
602
603 pub fn as_set_parameter(&self) -> Option<&SetParameter> {
605 match self {
606 CommandEnum::SetParameter(cmd) => Some(cmd),
607 _ => None,
608 }
609 }
610
611 pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
613 match self {
614 CommandEnum::Automaton(cmd) => Some(cmd),
615 _ => None,
616 }
617 }
618
619 pub fn as_sensor(&self) -> Option<&SensorCommand> {
621 match self {
622 CommandEnum::Sensor(cmd) => Some(cmd),
623 _ => None,
624 }
625 }
626
627 pub fn as_servo(&self) -> Option<&ServoCommand> {
629 match self {
630 CommandEnum::Servo(cmd) => Some(cmd),
631 _ => None,
632 }
633 }
634}
635
636impl fmt::Display for CommandEnum {
637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638 match self {
639 CommandEnum::SetParameter(cmd) => write!(f, "{}", cmd),
640 CommandEnum::Automaton(cmd) => write!(f, "{}", cmd),
641 CommandEnum::Sensor(cmd) => write!(f, "{}", cmd),
642 CommandEnum::Servo(cmd) => write!(f, "{}", cmd),
643 CommandEnum::System { kind, data } => {
644 write!(f, "System[{}] ({} bytes)", kind, data.len())
645 }
646 }
647 }
648}
649
650impl Command for CommandEnum {}
652
653pub trait ToCommand: Send + 'static {
657 type Command: Into<CommandEnum>;
659
660 fn to_command(self) -> Self::Command;
662}
663
664pub trait FromCommand: Sized {
666 type Command: TryInto<Self> + Clone;
668
669 fn from_command(cmd: Self::Command) -> Option<Self>;
671}
672
673impl From<SetParameter> for CommandEnum {
674 fn from(cmd: SetParameter) -> Self {
675 CommandEnum::SetParameter(cmd)
676 }
677}
678
679impl From<AutomatonCommand> for CommandEnum {
680 fn from(cmd: AutomatonCommand) -> Self {
681 CommandEnum::Automaton(cmd)
682 }
683}
684
685impl From<SensorCommand> for CommandEnum {
686 fn from(cmd: SensorCommand) -> Self {
687 CommandEnum::Sensor(cmd)
688 }
689}
690
691impl From<ServoCommand> for CommandEnum {
692 fn from(cmd: ServoCommand) -> Self {
693 CommandEnum::Servo(cmd)
694 }
695}
696
697impl TryFrom<CommandEnum> for SetParameter {
698 type Error = ();
699
700 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
701 match cmd {
702 CommandEnum::SetParameter(cmd) => Ok(cmd),
703 _ => Err(()),
704 }
705 }
706}
707
708impl TryFrom<CommandEnum> for AutomatonCommand {
709 type Error = ();
710
711 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
712 match cmd {
713 CommandEnum::Automaton(cmd) => Ok(cmd),
714 _ => Err(()),
715 }
716 }
717}
718
719impl TryFrom<CommandEnum> for SensorCommand {
720 type Error = ();
721
722 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
723 match cmd {
724 CommandEnum::Sensor(cmd) => Ok(cmd),
725 _ => Err(()),
726 }
727 }
728}
729
730impl TryFrom<CommandEnum> for ServoCommand {
731 type Error = ();
732
733 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
734 match cmd {
735 CommandEnum::Servo(cmd) => Ok(cmd),
736 _ => Err(()),
737 }
738 }
739}