1use crate::can::PiperFrame;
7use crate::protocol::{ProtocolError, i16_to_bytes_be, i32_to_bytes_be, ids::*};
8use bilge::prelude::*;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
20pub enum ControlModeCommand {
21 #[default]
23 Standby = 0x00,
24 CanControl = 0x01,
26 Teach = 0x02,
28 Ethernet = 0x03,
30 Wifi = 0x04,
32 OfflineTrajectory = 0x07,
34}
35
36impl TryFrom<u8> for ControlModeCommand {
37 type Error = ProtocolError;
38
39 fn try_from(value: u8) -> Result<Self, Self::Error> {
40 match value {
41 0x00 => Ok(ControlModeCommand::Standby),
42 0x01 => Ok(ControlModeCommand::CanControl),
43 0x02 => Ok(ControlModeCommand::Teach),
44 0x03 => Ok(ControlModeCommand::Ethernet),
45 0x04 => Ok(ControlModeCommand::Wifi),
46 0x07 => Ok(ControlModeCommand::OfflineTrajectory),
47 _ => Err(ProtocolError::InvalidValue {
48 field: "ControlModeCommand".to_string(),
49 value,
50 }),
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
57pub enum MitMode {
58 #[default]
60 PositionVelocity = 0x00,
61 Mit = 0xAD,
63}
64
65impl TryFrom<u8> for MitMode {
66 type Error = ProtocolError;
67
68 fn try_from(value: u8) -> Result<Self, Self::Error> {
69 match value {
70 0x00 => Ok(MitMode::PositionVelocity),
71 0xAD => Ok(MitMode::Mit),
72 _ => Err(ProtocolError::InvalidValue {
73 field: "MitMode".to_string(),
74 value,
75 }),
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
82pub enum InstallPosition {
83 #[default]
85 Invalid = 0x00,
86 Horizontal = 0x01,
88 SideLeft = 0x02,
90 SideRight = 0x03,
92}
93
94impl TryFrom<u8> for InstallPosition {
95 type Error = ProtocolError;
96
97 fn try_from(value: u8) -> Result<Self, Self::Error> {
98 match value {
99 0x00 => Ok(InstallPosition::Invalid),
100 0x01 => Ok(InstallPosition::Horizontal),
101 0x02 => Ok(InstallPosition::SideLeft),
102 0x03 => Ok(InstallPosition::SideRight),
103 _ => Err(ProtocolError::InvalidValue {
104 field: "InstallPosition".to_string(),
105 value,
106 }),
107 }
108 }
109}
110
111use crate::protocol::feedback::MoveMode;
113
114#[derive(Debug, Clone, Copy, Default)]
122pub struct ControlModeCommandFrame {
123 pub control_mode: ControlModeCommand, pub move_mode: MoveMode, pub speed_percent: u8, pub mit_mode: MitMode, pub trajectory_stay_time: u8, pub install_position: InstallPosition, }
131
132impl ControlModeCommandFrame {
133 pub fn mode_switch(control_mode: ControlModeCommand) -> Self {
137 Self {
138 control_mode,
139 move_mode: MoveMode::MoveP, speed_percent: 0,
141 mit_mode: MitMode::PositionVelocity,
142 trajectory_stay_time: 0,
143 install_position: InstallPosition::Invalid,
144 }
145 }
146
147 pub fn new(
149 control_mode: ControlModeCommand,
150 move_mode: MoveMode,
151 speed_percent: u8,
152 mit_mode: MitMode,
153 trajectory_stay_time: u8,
154 install_position: InstallPosition,
155 ) -> Self {
156 Self {
157 control_mode,
158 move_mode,
159 speed_percent,
160 mit_mode,
161 trajectory_stay_time,
162 install_position,
163 }
164 }
165
166 pub fn to_frame(self) -> PiperFrame {
168 let mut data = [0u8; 8];
169 data[0] = self.control_mode as u8;
170 data[1] = self.move_mode as u8;
171 data[2] = self.speed_percent;
172 data[3] = self.mit_mode as u8;
173 data[4] = self.trajectory_stay_time;
174 data[5] = self.install_position as u8;
175 PiperFrame::new_standard(ID_CONTROL_MODE as u16, &data)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
190 fn test_control_mode_command_from_u8() {
191 assert_eq!(
192 ControlModeCommand::try_from(0x00).unwrap(),
193 ControlModeCommand::Standby
194 );
195 assert_eq!(
196 ControlModeCommand::try_from(0x01).unwrap(),
197 ControlModeCommand::CanControl
198 );
199 assert_eq!(
200 ControlModeCommand::try_from(0x02).unwrap(),
201 ControlModeCommand::Teach
202 );
203 assert_eq!(
204 ControlModeCommand::try_from(0x03).unwrap(),
205 ControlModeCommand::Ethernet
206 );
207 assert_eq!(
208 ControlModeCommand::try_from(0x04).unwrap(),
209 ControlModeCommand::Wifi
210 );
211 assert_eq!(
212 ControlModeCommand::try_from(0x07).unwrap(),
213 ControlModeCommand::OfflineTrajectory
214 );
215 }
216
217 #[test]
218 fn test_control_mode_command_invalid_values() {
219 assert!(ControlModeCommand::try_from(0x05).is_err());
221 assert!(ControlModeCommand::try_from(0x06).is_err());
222 assert!(ControlModeCommand::try_from(0xFF).is_err());
223 }
224
225 #[test]
230 fn test_mit_mode_from_u8() {
231 assert_eq!(MitMode::try_from(0x00).unwrap(), MitMode::PositionVelocity);
232 assert_eq!(MitMode::try_from(0xAD).unwrap(), MitMode::Mit);
233 }
234
235 #[test]
236 fn test_mit_mode_invalid_values() {
237 assert!(MitMode::try_from(0x01).is_err());
238 assert!(MitMode::try_from(0xFF).is_err());
239 }
240
241 #[test]
246 fn test_install_position_from_u8() {
247 assert_eq!(
248 InstallPosition::try_from(0x00).unwrap(),
249 InstallPosition::Invalid
250 );
251 assert_eq!(
252 InstallPosition::try_from(0x01).unwrap(),
253 InstallPosition::Horizontal
254 );
255 assert_eq!(
256 InstallPosition::try_from(0x02).unwrap(),
257 InstallPosition::SideLeft
258 );
259 assert_eq!(
260 InstallPosition::try_from(0x03).unwrap(),
261 InstallPosition::SideRight
262 );
263 }
264
265 #[test]
266 fn test_install_position_invalid_values() {
267 assert!(InstallPosition::try_from(0x04).is_err());
268 assert!(InstallPosition::try_from(0xFF).is_err());
269 }
270
271 #[test]
276 fn test_control_mode_command_frame_mode_switch() {
277 let cmd = ControlModeCommandFrame::mode_switch(ControlModeCommand::CanControl);
278 let frame = cmd.to_frame();
279
280 assert_eq!(frame.id, ID_CONTROL_MODE);
281 assert_eq!(frame.data[0], 0x01); assert_eq!(frame.data[1], 0x00); assert_eq!(frame.data[2], 0x00); assert_eq!(frame.data[3], 0x00); assert_eq!(frame.data[4], 0x00); assert_eq!(frame.data[5], 0x00); assert_eq!(frame.data[6], 0x00); assert_eq!(frame.data[7], 0x00); }
290
291 #[test]
292 fn test_control_mode_command_frame_new() {
293 let cmd = ControlModeCommandFrame::new(
294 ControlModeCommand::CanControl,
295 MoveMode::MoveJ,
296 50, MitMode::Mit,
298 10, InstallPosition::Horizontal,
300 );
301 let frame = cmd.to_frame();
302
303 assert_eq!(frame.id, ID_CONTROL_MODE);
304 assert_eq!(frame.data[0], 0x01); assert_eq!(frame.data[1], 0x01); assert_eq!(frame.data[2], 50); assert_eq!(frame.data[3], 0xAD); assert_eq!(frame.data[4], 10); assert_eq!(frame.data[5], 0x01); }
311
312 #[test]
313 fn test_control_mode_command_frame_trajectory_terminate() {
314 let cmd = ControlModeCommandFrame::new(
316 ControlModeCommand::OfflineTrajectory,
317 MoveMode::MoveP,
318 0,
319 MitMode::PositionVelocity,
320 255, InstallPosition::Invalid,
322 );
323 let frame = cmd.to_frame();
324
325 assert_eq!(frame.data[4], 255); }
327}
328
329#[derive(Debug, Clone, Copy, Default)]
338pub struct JointControl12 {
339 pub j1_deg: i32, pub j2_deg: i32, }
342
343impl JointControl12 {
344 pub fn new(j1: f64, j2: f64) -> Self {
346 Self {
347 j1_deg: (j1 * 1000.0) as i32,
348 j2_deg: (j2 * 1000.0) as i32,
349 }
350 }
351
352 pub fn to_frame(self) -> PiperFrame {
354 let mut data = [0u8; 8];
355 let j1_bytes = i32_to_bytes_be(self.j1_deg);
356 let j2_bytes = i32_to_bytes_be(self.j2_deg);
357 data[0..4].copy_from_slice(&j1_bytes);
358 data[4..8].copy_from_slice(&j2_bytes);
359
360 PiperFrame::new_standard(ID_JOINT_CONTROL_12 as u16, &data)
361 }
362}
363
364#[derive(Debug, Clone, Copy, Default)]
368pub struct JointControl34 {
369 pub j3_deg: i32, pub j4_deg: i32, }
372
373impl JointControl34 {
374 pub fn new(j3: f64, j4: f64) -> Self {
376 Self {
377 j3_deg: (j3 * 1000.0) as i32,
378 j4_deg: (j4 * 1000.0) as i32,
379 }
380 }
381
382 pub fn to_frame(self) -> PiperFrame {
384 let mut data = [0u8; 8];
385 let j3_bytes = i32_to_bytes_be(self.j3_deg);
386 let j4_bytes = i32_to_bytes_be(self.j4_deg);
387 data[0..4].copy_from_slice(&j3_bytes);
388 data[4..8].copy_from_slice(&j4_bytes);
389
390 PiperFrame::new_standard(ID_JOINT_CONTROL_34 as u16, &data)
391 }
392}
393
394#[derive(Debug, Clone, Copy, Default)]
398pub struct JointControl56 {
399 pub j5_deg: i32, pub j6_deg: i32, }
402
403impl JointControl56 {
404 pub fn new(j5: f64, j6: f64) -> Self {
406 Self {
407 j5_deg: (j5 * 1000.0) as i32,
408 j6_deg: (j6 * 1000.0) as i32,
409 }
410 }
411
412 pub fn to_frame(self) -> PiperFrame {
414 let mut data = [0u8; 8];
415 let j5_bytes = i32_to_bytes_be(self.j5_deg);
416 let j6_bytes = i32_to_bytes_be(self.j6_deg);
417 data[0..4].copy_from_slice(&j5_bytes);
418 data[4..8].copy_from_slice(&j6_bytes);
419
420 PiperFrame::new_standard(ID_JOINT_CONTROL_56 as u16, &data)
421 }
422}
423
424#[cfg(test)]
425mod joint_control_tests {
426 use super::*;
427
428 #[test]
429 fn test_joint_control12_new() {
430 let cmd = JointControl12::new(90.0, -45.0);
431 assert_eq!(cmd.j1_deg, 90000);
432 assert_eq!(cmd.j2_deg, -45000);
433 }
434
435 #[test]
436 fn test_joint_control12_to_frame() {
437 let cmd = JointControl12::new(90.0, -45.0);
438 let frame = cmd.to_frame();
439
440 assert_eq!(frame.id, ID_JOINT_CONTROL_12);
441 let j1_decoded =
443 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
444 let j2_decoded =
445 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
446 assert_eq!(j1_decoded, 90000);
447 assert_eq!(j2_decoded, -45000);
448 }
449
450 #[test]
451 fn test_joint_control12_roundtrip() {
452 let cmd = JointControl12::new(90.0, -45.0);
454 let frame = cmd.to_frame();
455
456 assert_eq!(frame.id, ID_JOINT_CONTROL_12);
458
459 let j1_decoded =
461 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
462 let j2_decoded =
463 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
464 assert_eq!(j1_decoded, 90000);
465 assert_eq!(j2_decoded, -45000);
466
467 assert_eq!(cmd.j1_deg, 90000);
469 assert_eq!(cmd.j2_deg, -45000);
470 }
471
472 #[test]
473 fn test_joint_control34_new() {
474 let cmd = JointControl34::new(30.0, -60.0);
475 assert_eq!(cmd.j3_deg, 30000);
476 assert_eq!(cmd.j4_deg, -60000);
477 }
478
479 #[test]
480 fn test_joint_control34_to_frame() {
481 let cmd = JointControl34::new(30.0, -60.0);
482 let frame = cmd.to_frame();
483
484 assert_eq!(frame.id, ID_JOINT_CONTROL_34);
485 let j3_decoded =
486 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
487 let j4_decoded =
488 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
489 assert_eq!(j3_decoded, 30000);
490 assert_eq!(j4_decoded, -60000);
491 }
492
493 #[test]
494 fn test_joint_control56_new() {
495 let cmd = JointControl56::new(180.0, -90.0);
496 assert_eq!(cmd.j5_deg, 180000);
497 assert_eq!(cmd.j6_deg, -90000);
498 }
499
500 #[test]
501 fn test_joint_control56_to_frame() {
502 let cmd = JointControl56::new(180.0, -90.0);
503 let frame = cmd.to_frame();
504
505 assert_eq!(frame.id, ID_JOINT_CONTROL_56);
506 let j5_decoded =
507 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
508 let j6_decoded =
509 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
510 assert_eq!(j5_decoded, 180000);
511 assert_eq!(j6_decoded, -90000);
512 }
513
514 #[test]
515 fn test_joint_control_precision() {
516 let cmd = JointControl12::new(0.5, -0.5);
518 assert_eq!(cmd.j1_deg, 500);
519 assert_eq!(cmd.j2_deg, -500);
520 }
521}
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
529pub enum EmergencyStopAction {
530 #[default]
532 Invalid = 0x00,
533 EmergencyStop = 0x01,
535 Resume = 0x02,
537}
538
539impl TryFrom<u8> for EmergencyStopAction {
540 type Error = ProtocolError;
541
542 fn try_from(value: u8) -> Result<Self, Self::Error> {
543 match value {
544 0x00 => Ok(EmergencyStopAction::Invalid),
545 0x01 => Ok(EmergencyStopAction::EmergencyStop),
546 0x02 => Ok(EmergencyStopAction::Resume),
547 _ => Err(ProtocolError::InvalidValue {
548 field: "EmergencyStopAction".to_string(),
549 value,
550 }),
551 }
552 }
553}
554
555#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
557pub enum TrajectoryCommand {
558 #[default]
560 Closed = 0x00,
561 PausePlanning = 0x01,
563 StartContinue = 0x02,
565 ClearCurrent = 0x03,
567 ClearAll = 0x04,
569 GetCurrentPlanning = 0x05,
571 Terminate = 0x06,
573 Transmit = 0x07,
575 TransmitEnd = 0x08,
577}
578
579impl TryFrom<u8> for TrajectoryCommand {
580 type Error = ProtocolError;
581
582 fn try_from(value: u8) -> Result<Self, Self::Error> {
583 match value {
584 0x00 => Ok(TrajectoryCommand::Closed),
585 0x01 => Ok(TrajectoryCommand::PausePlanning),
586 0x02 => Ok(TrajectoryCommand::StartContinue),
587 0x03 => Ok(TrajectoryCommand::ClearCurrent),
588 0x04 => Ok(TrajectoryCommand::ClearAll),
589 0x05 => Ok(TrajectoryCommand::GetCurrentPlanning),
590 0x06 => Ok(TrajectoryCommand::Terminate),
591 0x07 => Ok(TrajectoryCommand::Transmit),
592 0x08 => Ok(TrajectoryCommand::TransmitEnd),
593 _ => Err(ProtocolError::InvalidValue {
594 field: "TrajectoryCommand".to_string(),
595 value,
596 }),
597 }
598 }
599}
600
601#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
603pub enum TeachCommand {
604 #[default]
606 Closed = 0x00,
607 StartRecord = 0x01,
609 EndRecord = 0x02,
611 Execute = 0x03,
613 Pause = 0x04,
615 Continue = 0x05,
617 Terminate = 0x06,
619 MoveToStart = 0x07,
621}
622
623impl TryFrom<u8> for TeachCommand {
624 type Error = ProtocolError;
625
626 fn try_from(value: u8) -> Result<Self, Self::Error> {
627 match value {
628 0x00 => Ok(TeachCommand::Closed),
629 0x01 => Ok(TeachCommand::StartRecord),
630 0x02 => Ok(TeachCommand::EndRecord),
631 0x03 => Ok(TeachCommand::Execute),
632 0x04 => Ok(TeachCommand::Pause),
633 0x05 => Ok(TeachCommand::Continue),
634 0x06 => Ok(TeachCommand::Terminate),
635 0x07 => Ok(TeachCommand::MoveToStart),
636 _ => Err(ProtocolError::InvalidValue {
637 field: "TeachCommand".to_string(),
638 value,
639 }),
640 }
641 }
642}
643
644#[derive(Debug, Clone, Copy, Default)]
650pub struct EmergencyStopCommand {
651 pub emergency_stop: EmergencyStopAction, pub trajectory_command: TrajectoryCommand, pub teach_command: TeachCommand, pub trajectory_index: u8, pub name_index: u16, pub crc16: u16, }
659
660impl EmergencyStopCommand {
661 pub fn emergency_stop() -> Self {
663 Self {
664 emergency_stop: EmergencyStopAction::EmergencyStop,
665 trajectory_command: TrajectoryCommand::Closed,
666 teach_command: TeachCommand::Closed,
667 trajectory_index: 0,
668 name_index: 0,
669 crc16: 0,
670 }
671 }
672
673 pub fn resume() -> Self {
675 Self {
676 emergency_stop: EmergencyStopAction::Resume,
677 trajectory_command: TrajectoryCommand::Closed,
678 teach_command: TeachCommand::Closed,
679 trajectory_index: 0,
680 name_index: 0,
681 crc16: 0,
682 }
683 }
684
685 pub fn trajectory_transmit(trajectory_index: u8, name_index: u16, crc16: u16) -> Self {
687 Self {
688 emergency_stop: EmergencyStopAction::Invalid,
689 trajectory_command: TrajectoryCommand::Transmit,
690 teach_command: TeachCommand::Closed,
691 trajectory_index,
692 name_index,
693 crc16,
694 }
695 }
696
697 pub fn to_frame(self) -> PiperFrame {
699 let mut data = [0u8; 8];
700 data[0] = self.emergency_stop as u8;
701 data[1] = self.trajectory_command as u8;
702 data[2] = self.teach_command as u8;
703 data[3] = self.trajectory_index;
704
705 let name_index_bytes = self.name_index.to_be_bytes();
707 data[4] = name_index_bytes[0];
708 data[5] = name_index_bytes[1];
709
710 let crc_bytes = self.crc16.to_be_bytes();
711 data[6] = crc_bytes[0];
712 data[7] = crc_bytes[1];
713
714 PiperFrame::new_standard(ID_EMERGENCY_STOP as u16, &data)
715 }
716}
717
718#[cfg(test)]
719mod emergency_stop_tests {
720 use super::*;
721
722 #[test]
723 fn test_emergency_stop_action_from_u8() {
724 assert_eq!(
725 EmergencyStopAction::try_from(0x00).unwrap(),
726 EmergencyStopAction::Invalid
727 );
728 assert_eq!(
729 EmergencyStopAction::try_from(0x01).unwrap(),
730 EmergencyStopAction::EmergencyStop
731 );
732 assert_eq!(
733 EmergencyStopAction::try_from(0x02).unwrap(),
734 EmergencyStopAction::Resume
735 );
736 }
737
738 #[test]
739 fn test_trajectory_command_from_u8() {
740 assert_eq!(
741 TrajectoryCommand::try_from(0x00).unwrap(),
742 TrajectoryCommand::Closed
743 );
744 assert_eq!(
745 TrajectoryCommand::try_from(0x07).unwrap(),
746 TrajectoryCommand::Transmit
747 );
748 assert_eq!(
749 TrajectoryCommand::try_from(0x08).unwrap(),
750 TrajectoryCommand::TransmitEnd
751 );
752 }
753
754 #[test]
755 fn test_teach_command_from_u8() {
756 assert_eq!(TeachCommand::try_from(0x00).unwrap(), TeachCommand::Closed);
757 assert_eq!(
758 TeachCommand::try_from(0x01).unwrap(),
759 TeachCommand::StartRecord
760 );
761 assert_eq!(
762 TeachCommand::try_from(0x07).unwrap(),
763 TeachCommand::MoveToStart
764 );
765 }
766
767 #[test]
768 fn test_emergency_stop_command_emergency_stop() {
769 let cmd = EmergencyStopCommand::emergency_stop();
770 let frame = cmd.to_frame();
771
772 assert_eq!(frame.id, ID_EMERGENCY_STOP);
773 assert_eq!(frame.data[0], 0x01); assert_eq!(frame.data[1], 0x00); assert_eq!(frame.data[2], 0x00); assert_eq!(frame.data[3], 0x00); }
778
779 #[test]
780 fn test_emergency_stop_command_resume() {
781 let cmd = EmergencyStopCommand::resume();
782 let frame = cmd.to_frame();
783
784 assert_eq!(frame.id, ID_EMERGENCY_STOP);
785 assert_eq!(frame.data[0], 0x02); }
787
788 #[test]
789 fn test_emergency_stop_command_trajectory_transmit() {
790 let cmd = EmergencyStopCommand::trajectory_transmit(5, 0x1234, 0x5678);
791 let frame = cmd.to_frame();
792
793 assert_eq!(frame.id, ID_EMERGENCY_STOP);
794 assert_eq!(frame.data[0], 0x00); assert_eq!(frame.data[1], 0x07); assert_eq!(frame.data[3], 5); let name_index = u16::from_be_bytes([frame.data[4], frame.data[5]]);
800 let crc16 = u16::from_be_bytes([frame.data[6], frame.data[7]]);
801 assert_eq!(name_index, 0x1234);
802 assert_eq!(crc16, 0x5678);
803 }
804}
805
806#[derive(Debug, Clone, Copy)]
814pub struct MotorEnableCommand {
815 pub joint_index: u8, pub enable: bool, }
818
819impl MotorEnableCommand {
820 pub fn enable(joint_index: u8) -> Self {
822 Self {
823 joint_index,
824 enable: true,
825 }
826 }
827
828 pub fn disable(joint_index: u8) -> Self {
830 Self {
831 joint_index,
832 enable: false,
833 }
834 }
835
836 pub fn enable_all() -> Self {
838 Self {
839 joint_index: 7,
840 enable: true,
841 }
842 }
843
844 pub fn disable_all() -> Self {
846 Self {
847 joint_index: 7,
848 enable: false,
849 }
850 }
851
852 pub fn to_frame(self) -> PiperFrame {
854 let mut data = [0u8; 8];
855 data[0] = self.joint_index;
856 data[1] = if self.enable { 0x02 } else { 0x01 };
857 PiperFrame::new_standard(ID_MOTOR_ENABLE as u16, &data)
860 }
861}
862
863#[cfg(test)]
864mod motor_enable_tests {
865 use super::*;
866
867 #[test]
868 fn test_motor_enable_command_enable() {
869 let cmd = MotorEnableCommand::enable(1);
870 let frame = cmd.to_frame();
871
872 assert_eq!(frame.id, ID_MOTOR_ENABLE);
873 assert_eq!(frame.data[0], 1);
874 assert_eq!(frame.data[1], 0x02); }
876
877 #[test]
878 fn test_motor_enable_command_disable() {
879 let cmd = MotorEnableCommand::disable(2);
880 let frame = cmd.to_frame();
881
882 assert_eq!(frame.id, ID_MOTOR_ENABLE);
883 assert_eq!(frame.data[0], 2);
884 assert_eq!(frame.data[1], 0x01); }
886
887 #[test]
888 fn test_motor_enable_command_enable_all() {
889 let cmd = MotorEnableCommand::enable_all();
890 let frame = cmd.to_frame();
891
892 assert_eq!(frame.id, ID_MOTOR_ENABLE);
893 assert_eq!(frame.data[0], 7); assert_eq!(frame.data[1], 0x02); }
896
897 #[test]
898 fn test_motor_enable_command_all_joints() {
899 for i in 1..=6 {
901 let cmd = MotorEnableCommand::enable(i);
902 let frame = cmd.to_frame();
903 assert_eq!(frame.data[0], i);
904 assert_eq!(frame.data[1], 0x02);
905 }
906 }
907}
908
909#[bitsize(8)]
920#[derive(FromBits, DebugBits, Clone, Copy, Default)]
921pub struct GripperControlFlags {
922 pub enable: bool, pub clear_error: bool, pub reserved: u6, }
926
927#[derive(Debug, Clone, Copy)]
933pub struct GripperControlCommand {
934 pub travel_mm: i32, pub torque_nm: i16, pub control_flags: GripperControlFlags, pub zero_setting: u8, }
939
940impl GripperControlCommand {
941 pub fn new(travel_mm: f64, torque_nm: f64, enable: bool) -> Self {
943 Self {
944 travel_mm: (travel_mm * 1000.0) as i32,
945 torque_nm: (torque_nm * 1000.0) as i16,
946 control_flags: {
947 let mut flags = GripperControlFlags::from(u8::new(0));
948 flags.set_enable(enable);
949 flags
950 },
951 zero_setting: 0x00,
952 }
953 }
954
955 pub fn set_zero_point(mut self) -> Self {
957 self.zero_setting = 0xAE;
958 let mut flags = GripperControlFlags::from(u8::new(0));
960 flags.set_enable(false);
961 self.control_flags = flags;
962 self
963 }
964
965 pub fn clear_error(mut self) -> Self {
967 let mut flags = self.control_flags;
968 flags.set_clear_error(true);
969 self.control_flags = flags;
970 self
971 }
972
973 pub fn to_frame(self) -> PiperFrame {
975 let mut data = [0u8; 8];
976
977 let travel_bytes = i32_to_bytes_be(self.travel_mm);
979 data[0..4].copy_from_slice(&travel_bytes);
980
981 let torque_bytes = i16_to_bytes_be(self.torque_nm);
982 data[4..6].copy_from_slice(&torque_bytes);
983
984 data[6] = u8::from(self.control_flags).value();
985 data[7] = self.zero_setting;
986
987 PiperFrame::new_standard(ID_GRIPPER_CONTROL as u16, &data)
988 }
989}
990
991#[cfg(test)]
992mod gripper_control_tests {
993 use super::*;
994
995 #[test]
996 fn test_gripper_control_flags_parse() {
997 let byte = 0b0000_0011;
999 let flags = GripperControlFlags::from(u8::new(byte));
1000
1001 assert!(flags.enable());
1002 assert!(flags.clear_error());
1003 }
1004
1005 #[test]
1006 fn test_gripper_control_flags_encode() {
1007 let mut flags = GripperControlFlags::from(u8::new(0));
1008 flags.set_enable(true);
1009 flags.set_clear_error(true);
1010
1011 let encoded = u8::from(flags).value();
1012 assert_eq!(encoded, 0b0000_0011);
1013 }
1014
1015 #[test]
1016 fn test_gripper_control_command_new() {
1017 let cmd = GripperControlCommand::new(50.0, 2.5, true);
1018 assert_eq!(cmd.travel_mm, 50000);
1019 assert_eq!(cmd.torque_nm, 2500);
1020 assert!(cmd.control_flags.enable());
1021 assert_eq!(cmd.zero_setting, 0x00);
1022 }
1023
1024 #[test]
1025 fn test_gripper_control_command_to_frame() {
1026 let cmd = GripperControlCommand::new(50.0, 2.5, true);
1027 let frame = cmd.to_frame();
1028
1029 assert_eq!(frame.id, ID_GRIPPER_CONTROL);
1030
1031 let travel_decoded =
1033 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1034 let torque_decoded = i16::from_be_bytes([frame.data[4], frame.data[5]]);
1035 assert_eq!(travel_decoded, 50000);
1036 assert_eq!(torque_decoded, 2500);
1037 assert_eq!(frame.data[6] & 0x01, 0x01); assert_eq!(frame.data[7], 0x00); }
1040
1041 #[test]
1042 fn test_gripper_control_command_set_zero_point() {
1043 let cmd = GripperControlCommand::new(0.0, 0.0, false).set_zero_point();
1044 let frame = cmd.to_frame();
1045
1046 assert_eq!(frame.data[6], 0x00); assert_eq!(frame.data[7], 0xAE); }
1049
1050 #[test]
1051 fn test_gripper_control_command_clear_error() {
1052 let cmd = GripperControlCommand::new(50.0, 2.5, true).clear_error();
1053 let frame = cmd.to_frame();
1054
1055 assert_eq!(frame.data[6] & 0x03, 0x03); }
1057
1058 #[test]
1059 fn test_gripper_control_command_fully_closed() {
1060 let cmd = GripperControlCommand::new(0.0, 1.0, true);
1062 assert_eq!(cmd.travel_mm, 0);
1063 }
1064}
1065
1066#[derive(Debug, Clone, Copy, Default)]
1075pub struct EndPoseControl1 {
1076 pub x_mm: i32, pub y_mm: i32, }
1079
1080impl EndPoseControl1 {
1081 pub fn new(x: f64, y: f64) -> Self {
1083 Self {
1084 x_mm: (x * 1000.0) as i32,
1085 y_mm: (y * 1000.0) as i32,
1086 }
1087 }
1088
1089 pub fn to_frame(self) -> PiperFrame {
1091 let mut data = [0u8; 8];
1092 let x_bytes = i32_to_bytes_be(self.x_mm);
1093 let y_bytes = i32_to_bytes_be(self.y_mm);
1094 data[0..4].copy_from_slice(&x_bytes);
1095 data[4..8].copy_from_slice(&y_bytes);
1096
1097 PiperFrame::new_standard(ID_END_POSE_CONTROL_1 as u16, &data)
1098 }
1099}
1100
1101#[derive(Debug, Clone, Copy, Default)]
1107pub struct EndPoseControl2 {
1108 pub z_mm: i32, pub rx_deg: i32, }
1111
1112impl EndPoseControl2 {
1113 pub fn new(z: f64, rx: f64) -> Self {
1115 Self {
1116 z_mm: (z * 1000.0) as i32,
1117 rx_deg: (rx * 1000.0) as i32,
1118 }
1119 }
1120
1121 pub fn to_frame(self) -> PiperFrame {
1123 let mut data = [0u8; 8];
1124 let z_bytes = i32_to_bytes_be(self.z_mm);
1125 let rx_bytes = i32_to_bytes_be(self.rx_deg);
1126 data[0..4].copy_from_slice(&z_bytes);
1127 data[4..8].copy_from_slice(&rx_bytes);
1128
1129 PiperFrame::new_standard(ID_END_POSE_CONTROL_2 as u16, &data)
1130 }
1131}
1132
1133#[derive(Debug, Clone, Copy, Default)]
1138pub struct EndPoseControl3 {
1139 pub ry_deg: i32, pub rz_deg: i32, }
1142
1143impl EndPoseControl3 {
1144 pub fn new(ry: f64, rz: f64) -> Self {
1146 Self {
1147 ry_deg: (ry * 1000.0) as i32,
1148 rz_deg: (rz * 1000.0) as i32,
1149 }
1150 }
1151
1152 pub fn to_frame(self) -> PiperFrame {
1154 let mut data = [0u8; 8];
1155 let ry_bytes = i32_to_bytes_be(self.ry_deg);
1156 let rz_bytes = i32_to_bytes_be(self.rz_deg);
1157 data[0..4].copy_from_slice(&ry_bytes);
1158 data[4..8].copy_from_slice(&rz_bytes);
1159
1160 PiperFrame::new_standard(ID_END_POSE_CONTROL_3 as u16, &data)
1161 }
1162}
1163
1164#[cfg(test)]
1165mod end_pose_control_tests {
1166 use super::*;
1167
1168 #[test]
1169 fn test_end_pose_control1_new() {
1170 let cmd = EndPoseControl1::new(100.0, -50.0);
1171 assert_eq!(cmd.x_mm, 100000);
1172 assert_eq!(cmd.y_mm, -50000);
1173 }
1174
1175 #[test]
1176 fn test_end_pose_control1_to_frame() {
1177 let cmd = EndPoseControl1::new(100.0, -50.0);
1178 let frame = cmd.to_frame();
1179
1180 assert_eq!(frame.id, ID_END_POSE_CONTROL_1);
1181 let x_decoded =
1182 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1183 let y_decoded =
1184 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
1185 assert_eq!(x_decoded, 100000);
1186 assert_eq!(y_decoded, -50000);
1187 }
1188
1189 #[test]
1190 fn test_end_pose_control2_new() {
1191 let cmd = EndPoseControl2::new(200.0, 90.0);
1192 assert_eq!(cmd.z_mm, 200000);
1193 assert_eq!(cmd.rx_deg, 90000);
1194 }
1195
1196 #[test]
1197 fn test_end_pose_control2_to_frame() {
1198 let cmd = EndPoseControl2::new(200.0, 90.0);
1199 let frame = cmd.to_frame();
1200
1201 assert_eq!(frame.id, ID_END_POSE_CONTROL_2);
1202 let z_decoded =
1203 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1204 let rx_decoded =
1205 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
1206 assert_eq!(z_decoded, 200000);
1207 assert_eq!(rx_decoded, 90000);
1208 }
1209
1210 #[test]
1211 fn test_end_pose_control3_new() {
1212 let cmd = EndPoseControl3::new(-45.0, 180.0);
1213 assert_eq!(cmd.ry_deg, -45000);
1214 assert_eq!(cmd.rz_deg, 180000);
1215 }
1216
1217 #[test]
1218 fn test_end_pose_control3_to_frame() {
1219 let cmd = EndPoseControl3::new(-45.0, 180.0);
1220 let frame = cmd.to_frame();
1221
1222 assert_eq!(frame.id, ID_END_POSE_CONTROL_3);
1223 let ry_decoded =
1224 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1225 let rz_decoded =
1226 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
1227 assert_eq!(ry_decoded, -45000);
1228 assert_eq!(rz_decoded, 180000);
1229 }
1230
1231 #[test]
1232 fn test_end_pose_control_precision() {
1233 let cmd = EndPoseControl1::new(0.5, -0.5);
1235 assert_eq!(cmd.x_mm, 500);
1236 assert_eq!(cmd.y_mm, -500);
1237 }
1238}
1239
1240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1246pub enum ArcPointIndex {
1247 Invalid = 0x00,
1249 Start = 0x01,
1251 Middle = 0x02,
1253 End = 0x03,
1255}
1256
1257impl TryFrom<u8> for ArcPointIndex {
1258 type Error = ProtocolError;
1259
1260 fn try_from(value: u8) -> Result<Self, Self::Error> {
1261 match value {
1262 0x00 => Ok(ArcPointIndex::Invalid),
1263 0x01 => Ok(ArcPointIndex::Start),
1264 0x02 => Ok(ArcPointIndex::Middle),
1265 0x03 => Ok(ArcPointIndex::End),
1266 _ => Err(ProtocolError::InvalidValue {
1267 field: "ArcPointIndex".to_string(),
1268 value,
1269 }),
1270 }
1271 }
1272}
1273
1274#[derive(Debug, Clone, Copy)]
1279pub struct ArcPointCommand {
1280 pub point_index: ArcPointIndex,
1281}
1282
1283impl ArcPointCommand {
1284 pub fn start() -> Self {
1286 Self {
1287 point_index: ArcPointIndex::Start,
1288 }
1289 }
1290
1291 pub fn middle() -> Self {
1293 Self {
1294 point_index: ArcPointIndex::Middle,
1295 }
1296 }
1297
1298 pub fn end() -> Self {
1300 Self {
1301 point_index: ArcPointIndex::End,
1302 }
1303 }
1304
1305 pub fn new(point_index: ArcPointIndex) -> Self {
1307 Self { point_index }
1308 }
1309
1310 pub fn to_frame(self) -> PiperFrame {
1312 let mut data = [0u8; 8];
1313 data[0] = self.point_index as u8;
1314 PiperFrame::new_standard(ID_ARC_POINT as u16, &data)
1317 }
1318}
1319
1320#[cfg(test)]
1321mod arc_point_tests {
1322 use super::*;
1323
1324 #[test]
1325 fn test_arc_point_index_from_u8() {
1326 assert_eq!(
1327 ArcPointIndex::try_from(0x00).unwrap(),
1328 ArcPointIndex::Invalid
1329 );
1330 assert_eq!(ArcPointIndex::try_from(0x01).unwrap(), ArcPointIndex::Start);
1331 assert_eq!(
1332 ArcPointIndex::try_from(0x02).unwrap(),
1333 ArcPointIndex::Middle
1334 );
1335 assert_eq!(ArcPointIndex::try_from(0x03).unwrap(), ArcPointIndex::End);
1336 }
1337
1338 #[test]
1339 fn test_arc_point_index_invalid() {
1340 assert!(ArcPointIndex::try_from(0x04).is_err());
1341 assert!(ArcPointIndex::try_from(0xFF).is_err());
1342 }
1343
1344 #[test]
1345 fn test_arc_point_command_start() {
1346 let cmd = ArcPointCommand::start();
1347 let frame = cmd.to_frame();
1348
1349 assert_eq!(frame.id, ID_ARC_POINT);
1350 assert_eq!(frame.data[0], 0x01);
1351 assert_eq!(frame.data[1], 0x00); }
1353
1354 #[test]
1355 fn test_arc_point_command_middle() {
1356 let cmd = ArcPointCommand::middle();
1357 let frame = cmd.to_frame();
1358
1359 assert_eq!(frame.id, ID_ARC_POINT);
1360 assert_eq!(frame.data[0], 0x02);
1361 }
1362
1363 #[test]
1364 fn test_arc_point_command_end() {
1365 let cmd = ArcPointCommand::end();
1366 let frame = cmd.to_frame();
1367
1368 assert_eq!(frame.id, ID_ARC_POINT);
1369 assert_eq!(frame.data[0], 0x03);
1370 }
1371}
1372
1373#[derive(Debug, Clone, Copy)]
1384pub struct MitControlCommand {
1385 pub joint_index: u8, pub pos_ref: f32, pub vel_ref: f32, pub kp: f32, pub kd: f32, pub t_ref: f32, pub crc: u8, }
1393
1394impl MitControlCommand {
1395 fn float_to_uint(x: f32, x_min: f32, x_max: f32, bits: u32) -> u32 {
1399 let span = x_max - x_min;
1400 let offset = x_min;
1401 if span <= 0.0 {
1402 return 0;
1403 }
1404 let result = ((x - offset) * ((1u32 << bits) - 1) as f32 / span) as u32;
1405 result.min((1u32 << bits) - 1)
1406 }
1407
1408 #[allow(dead_code)]
1414 pub fn uint_to_float(x_int: u32, x_min: f32, x_max: f32, bits: u32) -> f32 {
1415 let span = x_max - x_min;
1416 let offset = x_min;
1417 (x_int as f32) * span / ((1u32 << bits) - 1) as f32 + offset
1418 }
1419
1420 pub fn new(
1439 joint_index: u8,
1440 pos_ref: f32,
1441 vel_ref: f32,
1442 kp: f32,
1443 kd: f32,
1444 t_ref: f32,
1445 crc: u8,
1446 ) -> Self {
1447 Self {
1448 joint_index,
1449 pos_ref,
1450 vel_ref,
1451 kp,
1452 kd,
1453 t_ref,
1454 crc: crc & 0x0F, }
1456 }
1457
1458 pub fn to_frame(self) -> PiperFrame {
1476 let mut data = [0u8; 8];
1477
1478 let pos_ref_uint = Self::float_to_uint(self.pos_ref, -12.5, 12.5, 16);
1481 data[0] = ((pos_ref_uint >> 8) & 0xFF) as u8;
1482 data[1] = (pos_ref_uint & 0xFF) as u8;
1483
1484 let vel_ref_uint = Self::float_to_uint(self.vel_ref, -45.0, 45.0, 12);
1487 data[2] = ((vel_ref_uint >> 4) & 0xFF) as u8; let kp_uint = Self::float_to_uint(self.kp, 0.0, 500.0, 12);
1492 let vel_ref_low = (vel_ref_uint & 0x0F) as u8;
1493 let kp_high = ((kp_uint >> 8) & 0x0F) as u8;
1494 data[3] = (vel_ref_low << 4) | kp_high;
1495
1496 data[4] = (kp_uint & 0xFF) as u8;
1498
1499 let kd_uint = Self::float_to_uint(self.kd, -5.0, 5.0, 12);
1502 data[5] = ((kd_uint >> 4) & 0xFF) as u8; let t_ref_uint = Self::float_to_uint(self.t_ref, -18.0, 18.0, 8);
1507 let kd_low = (kd_uint & 0x0F) as u8;
1508 let t_ref_high = ((t_ref_uint >> 4) & 0x0F) as u8;
1509 data[6] = (kd_low << 4) | t_ref_high;
1510
1511 let t_ref_low = (t_ref_uint & 0x0F) as u8;
1513 let crc_low = self.crc & 0x0F;
1514 data[7] = (t_ref_low << 4) | crc_low;
1515
1516 let can_id = ID_MIT_CONTROL_BASE + (self.joint_index - 1) as u32;
1517 PiperFrame::new_standard(can_id as u16, &data)
1518 }
1519}
1520
1521#[cfg(test)]
1522mod mit_control_tests {
1523 use super::*;
1524
1525 #[test]
1526 fn test_float_to_uint() {
1527 let result = MitControlCommand::float_to_uint(5.0, 0.0, 10.0, 12);
1529 assert_eq!(result, 2047);
1531 }
1532
1533 #[test]
1534 fn test_float_to_uint_boundary() {
1535 let min = MitControlCommand::float_to_uint(0.0, 0.0, 10.0, 12);
1537 let max = MitControlCommand::float_to_uint(10.0, 0.0, 10.0, 12);
1538 assert_eq!(min, 0);
1539 assert_eq!(max, 4095);
1540 }
1541
1542 #[test]
1543 fn test_uint_to_float() {
1544 let result = MitControlCommand::uint_to_float(2047, 0.0, 10.0, 12);
1546 assert!((result - 5.0).abs() < 0.01);
1548 }
1549
1550 #[test]
1551 fn test_uint_to_float_boundary() {
1552 let min = MitControlCommand::uint_to_float(0, 0.0, 10.0, 12);
1554 let max = MitControlCommand::uint_to_float(4095, 0.0, 10.0, 12);
1555 assert!((min - 0.0).abs() < 0.001);
1556 assert!((max - 10.0).abs() < 0.001);
1557 }
1558
1559 #[test]
1560 fn test_mit_control_command_new() {
1561 let cmd = MitControlCommand::new(1, 1.0, 2.0, 10.0, 0.8, 5.0, 0x0A);
1562 assert_eq!(cmd.joint_index, 1);
1563 assert_eq!(cmd.pos_ref, 1.0);
1564 assert_eq!(cmd.vel_ref, 2.0);
1565 assert_eq!(cmd.kp, 10.0);
1566 assert_eq!(cmd.kd, 0.8);
1567 assert_eq!(cmd.t_ref, 5.0);
1568 assert_eq!(cmd.crc, 0x0A);
1569 }
1570
1571 #[test]
1572 fn test_mit_control_command_crc_mask() {
1573 let cmd = MitControlCommand::new(1, 0.0, 0.0, 0.0, 0.0, 0.0, 0xFF);
1575 assert_eq!(cmd.crc, 0x0F);
1576 }
1577
1578 #[test]
1579 fn test_mit_control_command_to_frame() {
1580 let cmd = MitControlCommand::new(1, 0.0, 0.0, 10.0, 0.8, 0.0, 0x05);
1582 let frame = cmd.to_frame();
1583
1584 assert_eq!(frame.id, ID_MIT_CONTROL_BASE);
1585 assert_eq!(frame.data[7] & 0x0F, 0x05);
1587 }
1588
1589 #[test]
1590 fn test_mit_control_command_with_official_ranges() {
1591 let cmd_min = MitControlCommand::new(1, -12.5, -45.0, 0.0, -5.0, -18.0, 0x00);
1600 let frame_min = cmd_min.to_frame();
1601 assert_eq!(frame_min.id, ID_MIT_CONTROL_BASE);
1602
1603 let cmd_max = MitControlCommand::new(1, 12.5, 45.0, 500.0, 5.0, 18.0, 0x0F);
1604 let frame_max = cmd_max.to_frame();
1605 assert_eq!(frame_max.id, ID_MIT_CONTROL_BASE);
1606
1607 let cmd_ref = MitControlCommand::new(1, 0.0, 0.0, 10.0, 0.8, 0.0, 0x00);
1609 let frame_ref = cmd_ref.to_frame();
1610 assert_eq!(frame_ref.id, ID_MIT_CONTROL_BASE);
1611 }
1612
1613 #[test]
1614 fn test_mit_control_command_all_joints() {
1615 for i in 1..=6 {
1617 let cmd = MitControlCommand::new(i, 0.0, 0.0, 10.0, 0.8, 0.0, 0x00);
1618 let frame = cmd.to_frame();
1619 let expected_id = ID_MIT_CONTROL_BASE + (i - 1) as u32;
1620 assert_eq!(frame.id, expected_id);
1621 }
1622 }
1623
1624 #[test]
1625 fn test_mit_control_command_roundtrip() {
1626 let original = 5.0f32;
1628 let x_min = 0.0f32;
1629 let x_max = 10.0f32;
1630 let bits = 12u32;
1631
1632 let uint_val = MitControlCommand::float_to_uint(original, x_min, x_max, bits);
1633 let float_val = MitControlCommand::uint_to_float(uint_val, x_min, x_max, bits);
1634
1635 assert!((float_val - original).abs() < 0.01);
1637 }
1638}
1639
1640#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1646pub enum LightControlEnable {
1647 #[default]
1649 Disabled = 0x00,
1650 Enabled = 0x01,
1652}
1653
1654impl From<u8> for LightControlEnable {
1655 fn from(value: u8) -> Self {
1656 match value {
1657 0x00 => Self::Disabled,
1658 0x01 => Self::Enabled,
1659 _ => Self::Disabled, }
1661 }
1662}
1663
1664#[derive(Debug, Clone, Copy)]
1668pub struct LightControlCommand {
1669 pub enable: LightControlEnable, pub joint_index: u8, pub led_index: u8, pub r: u8, pub g: u8, pub b: u8, pub counter: u8, }
1678
1679impl LightControlCommand {
1680 pub fn new(
1682 enable: LightControlEnable,
1683 joint_index: u8,
1684 led_index: u8,
1685 r: u8,
1686 g: u8,
1687 b: u8,
1688 counter: u8,
1689 ) -> Self {
1690 Self {
1691 enable,
1692 joint_index,
1693 led_index,
1694 r,
1695 g,
1696 b,
1697 counter,
1698 }
1699 }
1700
1701 pub fn to_frame(self) -> PiperFrame {
1703 let mut data = [0u8; 8];
1704 data[0] = self.enable as u8;
1705 data[1] = self.joint_index;
1706 data[2] = self.led_index;
1707 data[3] = self.r;
1708 data[4] = self.g;
1709 data[5] = self.b;
1710 data[7] = self.counter;
1712
1713 PiperFrame::new_standard(ID_LIGHT_CONTROL as u16, &data)
1714 }
1715}
1716
1717#[cfg(test)]
1718mod light_control_tests {
1719 use super::*;
1720
1721 #[test]
1722 fn test_light_control_enable_from() {
1723 assert_eq!(LightControlEnable::from(0x00), LightControlEnable::Disabled);
1724 assert_eq!(LightControlEnable::from(0x01), LightControlEnable::Enabled);
1725 assert_eq!(LightControlEnable::from(0xFF), LightControlEnable::Disabled); }
1727
1728 #[test]
1729 fn test_light_control_command_new() {
1730 let cmd = LightControlCommand::new(LightControlEnable::Enabled, 1, 0xFF, 255, 128, 0, 10);
1731 assert_eq!(cmd.enable, LightControlEnable::Enabled);
1732 assert_eq!(cmd.joint_index, 1);
1733 assert_eq!(cmd.led_index, 0xFF);
1734 assert_eq!(cmd.r, 255);
1735 assert_eq!(cmd.g, 128);
1736 assert_eq!(cmd.b, 0);
1737 assert_eq!(cmd.counter, 10);
1738 }
1739
1740 #[test]
1741 fn test_light_control_command_to_frame() {
1742 let cmd = LightControlCommand::new(LightControlEnable::Enabled, 2, 5, 100, 200, 50, 42);
1743 let frame = cmd.to_frame();
1744
1745 assert_eq!(frame.id, ID_LIGHT_CONTROL);
1746 assert_eq!(frame.data[0], 0x01); assert_eq!(frame.data[1], 2); assert_eq!(frame.data[2], 5); assert_eq!(frame.data[3], 100); assert_eq!(frame.data[4], 200); assert_eq!(frame.data[5], 50); assert_eq!(frame.data[6], 0x00); assert_eq!(frame.data[7], 42); }
1755
1756 #[test]
1757 fn test_light_control_command_all_leds() {
1758 let cmd = LightControlCommand::new(LightControlEnable::Enabled, 3, 0xFF, 255, 255, 255, 0);
1760 let frame = cmd.to_frame();
1761 assert_eq!(frame.data[2], 0xFF);
1762 }
1763
1764 #[test]
1765 fn test_light_control_command_disabled() {
1766 let cmd = LightControlCommand::new(LightControlEnable::Disabled, 1, 0, 0, 0, 0, 0);
1767 let frame = cmd.to_frame();
1768 assert_eq!(frame.data[0], 0x00);
1769 }
1770}