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)]
57pub enum SignalSource {
58 Automaton(String),
59 Sensor(String),
60 Servo(String),
61 External(String),
62 Manual,
63 Script,
64}
65
66impl SignalSource {
67 pub fn name(&self) -> &str {
68 match self {
69 SignalSource::Automaton(name) => name,
70 SignalSource::Sensor(name) => name,
71 SignalSource::Servo(name) => name,
72 SignalSource::External(name) => name,
73 SignalSource::Manual => "manual",
74 SignalSource::Script => "script",
75 }
76 }
77
78 pub fn kind(&self) -> &'static str {
79 match self {
80 SignalSource::Automaton(_) => "automaton",
81 SignalSource::Sensor(_) => "sensor",
82 SignalSource::Servo(_) => "servo",
83 SignalSource::External(_) => "external",
84 SignalSource::Manual => "manual",
85 SignalSource::Script => "script",
86 }
87 }
88}
89
90impl fmt::Display for SignalSource {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 SignalSource::Automaton(name) => write!(f, "⚙️ {}", name),
94 SignalSource::Sensor(name) => write!(f, "👁️ {}", name),
95 SignalSource::Servo(name) => write!(f, "🦾 {}", name),
96 SignalSource::External(name) => write!(f, "🌍 {}", name),
97 SignalSource::Manual => write!(f, "👤 manual"),
98 SignalSource::Script => write!(f, "📜 script"),
99 }
100 }
101}
102
103#[derive(Debug, Clone)]
107pub struct SetParameter {
108 pub port: PortId,
109 pub parameter: ParameterId,
110 pub value: f32,
111 pub source: SignalSource,
112 pub timestamp: u64,
113}
114
115impl SetParameter {
116 pub fn new(port: PortId, parameter: ParameterId, value: f32, source: SignalSource) -> Self {
117 Self {
118 port,
119 parameter,
120 value,
121 source,
122 timestamp: Self::now(),
123 }
124 }
125
126 pub fn with_timestamp(
127 port: PortId,
128 parameter: ParameterId,
129 value: f32,
130 source: SignalSource,
131 timestamp: u64,
132 ) -> Self {
133 Self {
134 port,
135 parameter,
136 value,
137 source,
138 timestamp,
139 }
140 }
141
142 pub fn now() -> u64 {
143 SystemTime::now()
144 .duration_since(UNIX_EPOCH)
145 .unwrap_or_default()
146 .as_micros() as u64
147 }
148}
149
150impl PartialEq for SetParameter {
151 fn eq(&self, other: &Self) -> bool {
152 self.port == other.port
153 && self.parameter == other.parameter
154 && (self.value - other.value).abs() < f32::EPSILON
155 && self.source == other.source
156 }
157}
158
159impl fmt::Display for SetParameter {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 write!(
162 f,
163 "[{}] {} → {}::{} = {:.3}",
164 self.timestamp, self.source, self.port, self.parameter, self.value
165 )
166 }
167}
168
169impl Command for SetParameter {}
171
172#[derive(Debug, Clone)]
175pub enum AutomatonCommand {
176 SetEnabled {
177 id: String,
178 enabled: bool,
179 },
180 SetParameter {
181 id: String,
182 name: String,
183 value: f32,
184 },
185 Reset {
186 id: String,
187 },
188 Connect {
189 from: String,
190 to: String,
191 gain: f32,
192 },
193 Disconnect {
194 from: String,
195 to: String,
196 },
197 Create {
198 kind: String,
199 id: String,
200 params: Vec<(String, f32)>,
201 },
202 Destroy {
203 id: String,
204 },
205}
206
207impl AutomatonCommand {
208 pub fn automaton_id(&self) -> Option<&str> {
209 match self {
210 AutomatonCommand::SetEnabled { id, .. } => Some(id),
211 AutomatonCommand::SetParameter { id, .. } => Some(id),
212 AutomatonCommand::Reset { id } => Some(id),
213 AutomatonCommand::Connect { from, to: _to, .. } => Some(from),
214 AutomatonCommand::Disconnect { from, to: _to } => Some(from),
215 AutomatonCommand::Create { id, .. } => Some(id),
216 AutomatonCommand::Destroy { id } => Some(id),
217 }
218 }
219}
220
221impl fmt::Display for AutomatonCommand {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 match self {
224 AutomatonCommand::SetEnabled { id, enabled } => {
225 write!(f, "Automaton[{}] set_enabled({})", id, enabled)
226 }
227 AutomatonCommand::SetParameter { id, name, value } => {
228 write!(f, "Automaton[{}] set_param({}={:.2})", id, name, value)
229 }
230 AutomatonCommand::Reset { id } => {
231 write!(f, "Automaton[{}] reset()", id)
232 }
233 AutomatonCommand::Connect { from, to, gain } => {
234 write!(f, "Automaton connect {} → {} gain={:.2}", from, to, gain)
235 }
236 AutomatonCommand::Disconnect { from, to } => {
237 write!(f, "Automaton disconnect {} → {}", from, to)
238 }
239 AutomatonCommand::Create { kind, id, params } => {
240 write!(
241 f,
242 "Automaton create {} as {} with {} params",
243 kind,
244 id,
245 params.len()
246 )
247 }
248 AutomatonCommand::Destroy { id } => {
249 write!(f, "Automaton destroy {}", id)
250 }
251 }
252 }
253}
254
255impl Command for AutomatonCommand {}
256
257#[derive(Debug, Clone)]
260pub enum CalibrationKind {
261 Auto,
262 SetCurrentAsMin,
263 SetCurrentAsMax,
264 Reset,
265}
266
267impl fmt::Display for CalibrationKind {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 match self {
270 CalibrationKind::Auto => write!(f, "auto"),
271 CalibrationKind::SetCurrentAsMin => write!(f, "set_min"),
272 CalibrationKind::SetCurrentAsMax => write!(f, "set_max"),
273 CalibrationKind::Reset => write!(f, "reset"),
274 }
275 }
276}
277
278#[derive(Debug, Clone)]
279pub enum SensorCommand {
280 StartListening { id: String, source: String },
281 StopListening { id: String },
282 SetSensitivity { id: String, value: f32 },
283 Calibrate { id: String, kind: CalibrationKind },
284 SetEnabled { id: String, enabled: bool },
285}
286
287impl SensorCommand {
288 pub fn sensor_id(&self) -> &str {
289 match self {
290 SensorCommand::StartListening { id, .. } => id,
291 SensorCommand::StopListening { id } => id,
292 SensorCommand::SetSensitivity { id, .. } => id,
293 SensorCommand::Calibrate { id, .. } => id,
294 SensorCommand::SetEnabled { id, .. } => id,
295 }
296 }
297}
298
299impl fmt::Display for SensorCommand {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 match self {
302 SensorCommand::StartListening { id, source } => {
303 write!(f, "Sensor[{}] start listening to {}", id, source)
304 }
305 SensorCommand::StopListening { id } => {
306 write!(f, "Sensor[{}] stop listening", id)
307 }
308 SensorCommand::SetSensitivity { id, value } => {
309 write!(f, "Sensor[{}] set sensitivity to {:.2}", id, value)
310 }
311 SensorCommand::Calibrate { id, kind } => {
312 write!(f, "Sensor[{}] calibrate {}", id, kind)
313 }
314 SensorCommand::SetEnabled { id, enabled } => {
315 write!(f, "Sensor[{}] set enabled({})", id, enabled)
316 }
317 }
318 }
319}
320
321impl Command for SensorCommand {}
322
323#[derive(Debug, Clone)]
326pub enum MappingType {
327 Linear,
328 Exponential,
329 Logarithmic,
330 Inverted,
331 Custom(String),
332}
333
334impl fmt::Display for MappingType {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 match self {
337 MappingType::Linear => write!(f, "linear"),
338 MappingType::Exponential => write!(f, "exponential"),
339 MappingType::Logarithmic => write!(f, "logarithmic"),
340 MappingType::Inverted => write!(f, "inverted"),
341 MappingType::Custom(s) => write!(f, "custom({})", s),
342 }
343 }
344}
345
346#[derive(Debug, Clone)]
347pub enum ServoCommand {
348 BindToAutomaton {
349 servo_id: String,
350 automaton_id: String,
351 },
352 BindToParameter {
353 servo_id: String,
354 port: PortId,
355 parameter: ParameterId,
356 },
357 Unbind {
358 servo_id: String,
359 },
360 SetRange {
361 servo_id: String,
362 min: f32,
363 max: f32,
364 },
365 SetMapping {
366 servo_id: String,
367 mapping: MappingType,
368 },
369 SetEnabled {
370 servo_id: String,
371 enabled: bool,
372 },
373}
374
375impl ServoCommand {
376 pub fn servo_id(&self) -> &str {
377 match self {
378 ServoCommand::BindToAutomaton { servo_id, .. } => servo_id,
379 ServoCommand::BindToParameter { servo_id, .. } => servo_id,
380 ServoCommand::Unbind { servo_id } => servo_id,
381 ServoCommand::SetRange { servo_id, .. } => servo_id,
382 ServoCommand::SetMapping { servo_id, .. } => servo_id,
383 ServoCommand::SetEnabled { servo_id, .. } => servo_id,
384 }
385 }
386}
387
388impl fmt::Display for ServoCommand {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 match self {
391 ServoCommand::BindToAutomaton {
392 servo_id,
393 automaton_id,
394 } => {
395 write!(f, "Servo[{}] bind to automaton {}", servo_id, automaton_id)
396 }
397 ServoCommand::BindToParameter {
398 servo_id,
399 port,
400 parameter,
401 } => {
402 write!(f, "Servo[{}] bind to {}::{}", servo_id, port, parameter)
403 }
404 ServoCommand::Unbind { servo_id } => {
405 write!(f, "Servo[{}] unbind", servo_id)
406 }
407 ServoCommand::SetRange { servo_id, min, max } => {
408 write!(f, "Servo[{}] set range [{}, {}]", servo_id, min, max)
409 }
410 ServoCommand::SetMapping { servo_id, mapping } => {
411 write!(f, "Servo[{}] set mapping {}", servo_id, mapping)
412 }
413 ServoCommand::SetEnabled { servo_id, enabled } => {
414 write!(f, "Servo[{}] set enabled({})", servo_id, enabled)
415 }
416 }
417 }
418}
419
420impl Command for ServoCommand {}
421
422#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
426pub enum CommandType {
427 SetParameter,
429 Automaton,
431 Sensor,
433 Servo,
435 System,
437}
438
439impl fmt::Display for CommandType {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 match self {
442 CommandType::SetParameter => write!(f, "SetParameter"),
443 CommandType::Automaton => write!(f, "Automaton"),
444 CommandType::Sensor => write!(f, "Sensor"),
445 CommandType::Servo => write!(f, "Servo"),
446 CommandType::System => write!(f, "System"),
447 }
448 }
449}
450
451#[derive(Debug, Clone)]
456pub enum CommandEnum {
457 SetParameter(SetParameter),
459 Automaton(AutomatonCommand),
461 Sensor(SensorCommand),
463 Servo(ServoCommand),
465 System { kind: String, data: Vec<u8> },
467}
468
469impl CommandEnum {
470 pub fn command_type(&self) -> CommandType {
472 match self {
473 CommandEnum::SetParameter(_) => CommandType::SetParameter,
474 CommandEnum::Automaton(_) => CommandType::Automaton,
475 CommandEnum::Sensor(_) => CommandType::Sensor,
476 CommandEnum::Servo(_) => CommandType::Servo,
477 CommandEnum::System { .. } => CommandType::System,
478 }
479 }
480
481 pub fn target_node_id(&self) -> Option<crate::traits::NodeId> {
483 match self {
484 CommandEnum::SetParameter(cmd) => Some(cmd.port.node_id()),
485 _ => None,
486 }
487 }
488
489 pub fn timestamp(&self) -> Option<u64> {
491 match self {
492 CommandEnum::SetParameter(cmd) => Some(cmd.timestamp),
493 _ => None,
494 }
495 }
496
497 pub fn as_set_parameter(&self) -> Option<&SetParameter> {
499 match self {
500 CommandEnum::SetParameter(cmd) => Some(cmd),
501 _ => None,
502 }
503 }
504
505 pub fn as_automaton(&self) -> Option<&AutomatonCommand> {
507 match self {
508 CommandEnum::Automaton(cmd) => Some(cmd),
509 _ => None,
510 }
511 }
512
513 pub fn as_sensor(&self) -> Option<&SensorCommand> {
515 match self {
516 CommandEnum::Sensor(cmd) => Some(cmd),
517 _ => None,
518 }
519 }
520
521 pub fn as_servo(&self) -> Option<&ServoCommand> {
523 match self {
524 CommandEnum::Servo(cmd) => Some(cmd),
525 _ => None,
526 }
527 }
528}
529
530impl fmt::Display for CommandEnum {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 match self {
533 CommandEnum::SetParameter(cmd) => write!(f, "{}", cmd),
534 CommandEnum::Automaton(cmd) => write!(f, "{}", cmd),
535 CommandEnum::Sensor(cmd) => write!(f, "{}", cmd),
536 CommandEnum::Servo(cmd) => write!(f, "{}", cmd),
537 CommandEnum::System { kind, data } => {
538 write!(f, "System[{}] ({} bytes)", kind, data.len())
539 }
540 }
541 }
542}
543
544impl Command for CommandEnum {}
546
547pub trait ToCommand: Send + 'static {
551 type Command: Into<CommandEnum>;
553
554 fn to_command(self) -> Self::Command;
556}
557
558pub trait FromCommand: Sized {
560 type Command: TryInto<Self> + Clone;
562
563 fn from_command(cmd: Self::Command) -> Option<Self>;
565}
566
567impl From<SetParameter> for CommandEnum {
568 fn from(cmd: SetParameter) -> Self {
569 CommandEnum::SetParameter(cmd)
570 }
571}
572
573impl From<AutomatonCommand> for CommandEnum {
574 fn from(cmd: AutomatonCommand) -> Self {
575 CommandEnum::Automaton(cmd)
576 }
577}
578
579impl From<SensorCommand> for CommandEnum {
580 fn from(cmd: SensorCommand) -> Self {
581 CommandEnum::Sensor(cmd)
582 }
583}
584
585impl From<ServoCommand> for CommandEnum {
586 fn from(cmd: ServoCommand) -> Self {
587 CommandEnum::Servo(cmd)
588 }
589}
590
591impl TryFrom<CommandEnum> for SetParameter {
592 type Error = ();
593
594 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
595 match cmd {
596 CommandEnum::SetParameter(cmd) => Ok(cmd),
597 _ => Err(()),
598 }
599 }
600}
601
602impl TryFrom<CommandEnum> for AutomatonCommand {
603 type Error = ();
604
605 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
606 match cmd {
607 CommandEnum::Automaton(cmd) => Ok(cmd),
608 _ => Err(()),
609 }
610 }
611}
612
613impl TryFrom<CommandEnum> for SensorCommand {
614 type Error = ();
615
616 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
617 match cmd {
618 CommandEnum::Sensor(cmd) => Ok(cmd),
619 _ => Err(()),
620 }
621 }
622}
623
624impl TryFrom<CommandEnum> for ServoCommand {
625 type Error = ();
626
627 fn try_from(cmd: CommandEnum) -> Result<Self, Self::Error> {
628 match cmd {
629 CommandEnum::Servo(cmd) => Ok(cmd),
630 _ => Err(()),
631 }
632 }
633}