1use crate::can::PiperFrame;
7use crate::{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::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 {
349 Self {
350 j1_deg: (j1 * 1000.0).round() as i32,
351 j2_deg: (j2 * 1000.0).round() as i32,
352 }
353 }
354
355 pub fn to_frame(self) -> PiperFrame {
357 let mut data = [0u8; 8];
358 let j1_bytes = i32_to_bytes_be(self.j1_deg);
359 let j2_bytes = i32_to_bytes_be(self.j2_deg);
360 data[0..4].copy_from_slice(&j1_bytes);
361 data[4..8].copy_from_slice(&j2_bytes);
362
363 PiperFrame::new_standard(ID_JOINT_CONTROL_12 as u16, &data)
364 }
365}
366
367#[derive(Debug, Clone, Copy, Default)]
371pub struct JointControl34 {
372 pub j3_deg: i32, pub j4_deg: i32, }
375
376impl JointControl34 {
377 pub fn new(j3: f64, j4: f64) -> Self {
382 Self {
383 j3_deg: (j3 * 1000.0).round() as i32,
384 j4_deg: (j4 * 1000.0).round() as i32,
385 }
386 }
387
388 pub fn to_frame(self) -> PiperFrame {
390 let mut data = [0u8; 8];
391 let j3_bytes = i32_to_bytes_be(self.j3_deg);
392 let j4_bytes = i32_to_bytes_be(self.j4_deg);
393 data[0..4].copy_from_slice(&j3_bytes);
394 data[4..8].copy_from_slice(&j4_bytes);
395
396 PiperFrame::new_standard(ID_JOINT_CONTROL_34 as u16, &data)
397 }
398}
399
400#[derive(Debug, Clone, Copy, Default)]
404pub struct JointControl56 {
405 pub j5_deg: i32, pub j6_deg: i32, }
408
409impl JointControl56 {
410 pub fn new(j5: f64, j6: f64) -> Self {
415 Self {
416 j5_deg: (j5 * 1000.0).round() as i32,
417 j6_deg: (j6 * 1000.0).round() as i32,
418 }
419 }
420
421 pub fn to_frame(self) -> PiperFrame {
423 let mut data = [0u8; 8];
424 let j5_bytes = i32_to_bytes_be(self.j5_deg);
425 let j6_bytes = i32_to_bytes_be(self.j6_deg);
426 data[0..4].copy_from_slice(&j5_bytes);
427 data[4..8].copy_from_slice(&j6_bytes);
428
429 PiperFrame::new_standard(ID_JOINT_CONTROL_56 as u16, &data)
430 }
431}
432
433#[cfg(test)]
434mod joint_control_tests {
435 use super::*;
436
437 #[test]
438 fn test_joint_control12_new() {
439 let cmd = JointControl12::new(90.0, -45.0);
440 assert_eq!(cmd.j1_deg, 90000);
441 assert_eq!(cmd.j2_deg, -45000);
442 }
443
444 #[test]
445 fn test_joint_control12_to_frame() {
446 let cmd = JointControl12::new(90.0, -45.0);
447 let frame = cmd.to_frame();
448
449 assert_eq!(frame.id, ID_JOINT_CONTROL_12);
450 let j1_decoded =
452 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
453 let j2_decoded =
454 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
455 assert_eq!(j1_decoded, 90000);
456 assert_eq!(j2_decoded, -45000);
457 }
458
459 #[test]
460 fn test_joint_control12_roundtrip() {
461 let cmd = JointControl12::new(90.0, -45.0);
463 let frame = cmd.to_frame();
464
465 assert_eq!(frame.id, ID_JOINT_CONTROL_12);
467
468 let j1_decoded =
470 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
471 let j2_decoded =
472 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
473 assert_eq!(j1_decoded, 90000);
474 assert_eq!(j2_decoded, -45000);
475
476 assert_eq!(cmd.j1_deg, 90000);
478 assert_eq!(cmd.j2_deg, -45000);
479 }
480
481 #[test]
482 fn test_joint_control34_new() {
483 let cmd = JointControl34::new(30.0, -60.0);
484 assert_eq!(cmd.j3_deg, 30000);
485 assert_eq!(cmd.j4_deg, -60000);
486 }
487
488 #[test]
489 fn test_joint_control34_to_frame() {
490 let cmd = JointControl34::new(30.0, -60.0);
491 let frame = cmd.to_frame();
492
493 assert_eq!(frame.id, ID_JOINT_CONTROL_34);
494 let j3_decoded =
495 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
496 let j4_decoded =
497 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
498 assert_eq!(j3_decoded, 30000);
499 assert_eq!(j4_decoded, -60000);
500 }
501
502 #[test]
503 fn test_joint_control56_new() {
504 let cmd = JointControl56::new(180.0, -90.0);
505 assert_eq!(cmd.j5_deg, 180000);
506 assert_eq!(cmd.j6_deg, -90000);
507 }
508
509 #[test]
510 fn test_joint_control56_to_frame() {
511 let cmd = JointControl56::new(180.0, -90.0);
512 let frame = cmd.to_frame();
513
514 assert_eq!(frame.id, ID_JOINT_CONTROL_56);
515 let j5_decoded =
516 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
517 let j6_decoded =
518 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
519 assert_eq!(j5_decoded, 180000);
520 assert_eq!(j6_decoded, -90000);
521 }
522
523 #[test]
524 fn test_joint_control_precision() {
525 let cmd = JointControl12::new(0.5, -0.5);
527 assert_eq!(cmd.j1_deg, 500);
528 assert_eq!(cmd.j2_deg, -500);
529 }
530}
531
532#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
538pub enum EmergencyStopAction {
539 #[default]
541 Invalid = 0x00,
542 EmergencyStop = 0x01,
544 Resume = 0x02,
546}
547
548impl TryFrom<u8> for EmergencyStopAction {
549 type Error = ProtocolError;
550
551 fn try_from(value: u8) -> Result<Self, Self::Error> {
552 match value {
553 0x00 => Ok(EmergencyStopAction::Invalid),
554 0x01 => Ok(EmergencyStopAction::EmergencyStop),
555 0x02 => Ok(EmergencyStopAction::Resume),
556 _ => Err(ProtocolError::InvalidValue {
557 field: "EmergencyStopAction".to_string(),
558 value,
559 }),
560 }
561 }
562}
563
564#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
566pub enum TrajectoryCommand {
567 #[default]
569 Closed = 0x00,
570 PausePlanning = 0x01,
572 StartContinue = 0x02,
574 ClearCurrent = 0x03,
576 ClearAll = 0x04,
578 GetCurrentPlanning = 0x05,
580 Terminate = 0x06,
582 Transmit = 0x07,
584 TransmitEnd = 0x08,
586}
587
588impl TryFrom<u8> for TrajectoryCommand {
589 type Error = ProtocolError;
590
591 fn try_from(value: u8) -> Result<Self, Self::Error> {
592 match value {
593 0x00 => Ok(TrajectoryCommand::Closed),
594 0x01 => Ok(TrajectoryCommand::PausePlanning),
595 0x02 => Ok(TrajectoryCommand::StartContinue),
596 0x03 => Ok(TrajectoryCommand::ClearCurrent),
597 0x04 => Ok(TrajectoryCommand::ClearAll),
598 0x05 => Ok(TrajectoryCommand::GetCurrentPlanning),
599 0x06 => Ok(TrajectoryCommand::Terminate),
600 0x07 => Ok(TrajectoryCommand::Transmit),
601 0x08 => Ok(TrajectoryCommand::TransmitEnd),
602 _ => Err(ProtocolError::InvalidValue {
603 field: "TrajectoryCommand".to_string(),
604 value,
605 }),
606 }
607 }
608}
609
610#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
612pub enum TeachCommand {
613 #[default]
615 Closed = 0x00,
616 StartRecord = 0x01,
618 EndRecord = 0x02,
620 Execute = 0x03,
622 Pause = 0x04,
624 Continue = 0x05,
626 Terminate = 0x06,
628 MoveToStart = 0x07,
630}
631
632impl TryFrom<u8> for TeachCommand {
633 type Error = ProtocolError;
634
635 fn try_from(value: u8) -> Result<Self, Self::Error> {
636 match value {
637 0x00 => Ok(TeachCommand::Closed),
638 0x01 => Ok(TeachCommand::StartRecord),
639 0x02 => Ok(TeachCommand::EndRecord),
640 0x03 => Ok(TeachCommand::Execute),
641 0x04 => Ok(TeachCommand::Pause),
642 0x05 => Ok(TeachCommand::Continue),
643 0x06 => Ok(TeachCommand::Terminate),
644 0x07 => Ok(TeachCommand::MoveToStart),
645 _ => Err(ProtocolError::InvalidValue {
646 field: "TeachCommand".to_string(),
647 value,
648 }),
649 }
650 }
651}
652
653#[derive(Debug, Clone, Copy, Default)]
659pub struct EmergencyStopCommand {
660 pub emergency_stop: EmergencyStopAction, pub trajectory_command: TrajectoryCommand, pub teach_command: TeachCommand, pub trajectory_index: u8, pub name_index: u16, pub crc16: u16, }
668
669impl EmergencyStopCommand {
670 pub fn emergency_stop() -> Self {
672 Self {
673 emergency_stop: EmergencyStopAction::EmergencyStop,
674 trajectory_command: TrajectoryCommand::Closed,
675 teach_command: TeachCommand::Closed,
676 trajectory_index: 0,
677 name_index: 0,
678 crc16: 0,
679 }
680 }
681
682 pub fn resume() -> Self {
684 Self {
685 emergency_stop: EmergencyStopAction::Resume,
686 trajectory_command: TrajectoryCommand::Closed,
687 teach_command: TeachCommand::Closed,
688 trajectory_index: 0,
689 name_index: 0,
690 crc16: 0,
691 }
692 }
693
694 pub fn trajectory_transmit(trajectory_index: u8, name_index: u16, crc16: u16) -> Self {
696 Self {
697 emergency_stop: EmergencyStopAction::Invalid,
698 trajectory_command: TrajectoryCommand::Transmit,
699 teach_command: TeachCommand::Closed,
700 trajectory_index,
701 name_index,
702 crc16,
703 }
704 }
705
706 pub fn to_frame(self) -> PiperFrame {
708 let mut data = [0u8; 8];
709 data[0] = self.emergency_stop as u8;
710 data[1] = self.trajectory_command as u8;
711 data[2] = self.teach_command as u8;
712 data[3] = self.trajectory_index;
713
714 let name_index_bytes = self.name_index.to_be_bytes();
716 data[4] = name_index_bytes[0];
717 data[5] = name_index_bytes[1];
718
719 let crc_bytes = self.crc16.to_be_bytes();
720 data[6] = crc_bytes[0];
721 data[7] = crc_bytes[1];
722
723 PiperFrame::new_standard(ID_EMERGENCY_STOP as u16, &data)
724 }
725}
726
727#[cfg(test)]
728mod emergency_stop_tests {
729 use super::*;
730
731 #[test]
732 fn test_emergency_stop_action_from_u8() {
733 assert_eq!(
734 EmergencyStopAction::try_from(0x00).unwrap(),
735 EmergencyStopAction::Invalid
736 );
737 assert_eq!(
738 EmergencyStopAction::try_from(0x01).unwrap(),
739 EmergencyStopAction::EmergencyStop
740 );
741 assert_eq!(
742 EmergencyStopAction::try_from(0x02).unwrap(),
743 EmergencyStopAction::Resume
744 );
745 }
746
747 #[test]
748 fn test_trajectory_command_from_u8() {
749 assert_eq!(
750 TrajectoryCommand::try_from(0x00).unwrap(),
751 TrajectoryCommand::Closed
752 );
753 assert_eq!(
754 TrajectoryCommand::try_from(0x07).unwrap(),
755 TrajectoryCommand::Transmit
756 );
757 assert_eq!(
758 TrajectoryCommand::try_from(0x08).unwrap(),
759 TrajectoryCommand::TransmitEnd
760 );
761 }
762
763 #[test]
764 fn test_teach_command_from_u8() {
765 assert_eq!(TeachCommand::try_from(0x00).unwrap(), TeachCommand::Closed);
766 assert_eq!(
767 TeachCommand::try_from(0x01).unwrap(),
768 TeachCommand::StartRecord
769 );
770 assert_eq!(
771 TeachCommand::try_from(0x07).unwrap(),
772 TeachCommand::MoveToStart
773 );
774 }
775
776 #[test]
777 fn test_emergency_stop_command_emergency_stop() {
778 let cmd = EmergencyStopCommand::emergency_stop();
779 let frame = cmd.to_frame();
780
781 assert_eq!(frame.id, ID_EMERGENCY_STOP);
782 assert_eq!(frame.data[0], 0x01); assert_eq!(frame.data[1], 0x00); assert_eq!(frame.data[2], 0x00); assert_eq!(frame.data[3], 0x00); }
787
788 #[test]
789 fn test_emergency_stop_command_resume() {
790 let cmd = EmergencyStopCommand::resume();
791 let frame = cmd.to_frame();
792
793 assert_eq!(frame.id, ID_EMERGENCY_STOP);
794 assert_eq!(frame.data[0], 0x02); }
796
797 #[test]
798 fn test_emergency_stop_command_trajectory_transmit() {
799 let cmd = EmergencyStopCommand::trajectory_transmit(5, 0x1234, 0x5678);
800 let frame = cmd.to_frame();
801
802 assert_eq!(frame.id, ID_EMERGENCY_STOP);
803 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]]);
809 let crc16 = u16::from_be_bytes([frame.data[6], frame.data[7]]);
810 assert_eq!(name_index, 0x1234);
811 assert_eq!(crc16, 0x5678);
812 }
813}
814
815#[derive(Debug, Clone, Copy)]
823pub struct MotorEnableCommand {
824 pub joint_index: u8, pub enable: bool, }
827
828impl MotorEnableCommand {
829 pub fn enable(joint_index: u8) -> Self {
831 Self {
832 joint_index,
833 enable: true,
834 }
835 }
836
837 pub fn disable(joint_index: u8) -> Self {
839 Self {
840 joint_index,
841 enable: false,
842 }
843 }
844
845 pub fn enable_all() -> Self {
847 Self {
848 joint_index: 7,
849 enable: true,
850 }
851 }
852
853 pub fn disable_all() -> Self {
855 Self {
856 joint_index: 7,
857 enable: false,
858 }
859 }
860
861 pub fn to_frame(self) -> PiperFrame {
863 let mut data = [0u8; 8];
864 data[0] = self.joint_index;
865 data[1] = if self.enable { 0x02 } else { 0x01 };
866 PiperFrame::new_standard(ID_MOTOR_ENABLE as u16, &data)
869 }
870}
871
872#[cfg(test)]
873mod motor_enable_tests {
874 use super::*;
875
876 #[test]
877 fn test_motor_enable_command_enable() {
878 let cmd = MotorEnableCommand::enable(1);
879 let frame = cmd.to_frame();
880
881 assert_eq!(frame.id, ID_MOTOR_ENABLE);
882 assert_eq!(frame.data[0], 1);
883 assert_eq!(frame.data[1], 0x02); }
885
886 #[test]
887 fn test_motor_enable_command_disable() {
888 let cmd = MotorEnableCommand::disable(2);
889 let frame = cmd.to_frame();
890
891 assert_eq!(frame.id, ID_MOTOR_ENABLE);
892 assert_eq!(frame.data[0], 2);
893 assert_eq!(frame.data[1], 0x01); }
895
896 #[test]
897 fn test_motor_enable_command_enable_all() {
898 let cmd = MotorEnableCommand::enable_all();
899 let frame = cmd.to_frame();
900
901 assert_eq!(frame.id, ID_MOTOR_ENABLE);
902 assert_eq!(frame.data[0], 7); assert_eq!(frame.data[1], 0x02); }
905
906 #[test]
907 fn test_motor_enable_command_all_joints() {
908 for i in 1..=6 {
910 let cmd = MotorEnableCommand::enable(i);
911 let frame = cmd.to_frame();
912 assert_eq!(frame.data[0], i);
913 assert_eq!(frame.data[1], 0x02);
914 }
915 }
916}
917
918#[bitsize(8)]
929#[derive(FromBits, DebugBits, Clone, Copy, Default)]
930pub struct GripperControlFlags {
931 pub enable: bool, pub clear_error: bool, pub reserved: u6, }
935
936#[derive(Debug, Clone, Copy)]
942pub struct GripperControlCommand {
943 pub travel_mm: i32, pub torque_nm: i16, pub control_flags: GripperControlFlags, pub zero_setting: u8, }
948
949impl GripperControlCommand {
950 pub fn new(travel_mm: f64, torque_nm: f64, enable: bool) -> Self {
952 Self {
953 travel_mm: (travel_mm * 1000.0) as i32,
954 torque_nm: (torque_nm * 1000.0) as i16,
955 control_flags: {
956 let mut flags = GripperControlFlags::from(u8::new(0));
957 flags.set_enable(enable);
958 flags
959 },
960 zero_setting: 0x00,
961 }
962 }
963
964 pub fn set_zero_point(mut self) -> Self {
966 self.zero_setting = 0xAE;
967 let mut flags = GripperControlFlags::from(u8::new(0));
969 flags.set_enable(false);
970 self.control_flags = flags;
971 self
972 }
973
974 pub fn clear_error(mut self) -> Self {
976 let mut flags = self.control_flags;
977 flags.set_clear_error(true);
978 self.control_flags = flags;
979 self
980 }
981
982 pub fn to_frame(self) -> PiperFrame {
984 let mut data = [0u8; 8];
985
986 let travel_bytes = i32_to_bytes_be(self.travel_mm);
988 data[0..4].copy_from_slice(&travel_bytes);
989
990 let torque_bytes = i16_to_bytes_be(self.torque_nm);
991 data[4..6].copy_from_slice(&torque_bytes);
992
993 data[6] = u8::from(self.control_flags).value();
994 data[7] = self.zero_setting;
995
996 PiperFrame::new_standard(ID_GRIPPER_CONTROL as u16, &data)
997 }
998}
999
1000#[cfg(test)]
1001mod gripper_control_tests {
1002 use super::*;
1003
1004 #[test]
1005 fn test_gripper_control_flags_parse() {
1006 let byte = 0b0000_0011;
1008 let flags = GripperControlFlags::from(u8::new(byte));
1009
1010 assert!(flags.enable());
1011 assert!(flags.clear_error());
1012 }
1013
1014 #[test]
1015 fn test_gripper_control_flags_encode() {
1016 let mut flags = GripperControlFlags::from(u8::new(0));
1017 flags.set_enable(true);
1018 flags.set_clear_error(true);
1019
1020 let encoded = u8::from(flags).value();
1021 assert_eq!(encoded, 0b0000_0011);
1022 }
1023
1024 #[test]
1025 fn test_gripper_control_command_new() {
1026 let cmd = GripperControlCommand::new(50.0, 2.5, true);
1027 assert_eq!(cmd.travel_mm, 50000);
1028 assert_eq!(cmd.torque_nm, 2500);
1029 assert!(cmd.control_flags.enable());
1030 assert_eq!(cmd.zero_setting, 0x00);
1031 }
1032
1033 #[test]
1034 fn test_gripper_control_command_to_frame() {
1035 let cmd = GripperControlCommand::new(50.0, 2.5, true);
1036 let frame = cmd.to_frame();
1037
1038 assert_eq!(frame.id, ID_GRIPPER_CONTROL);
1039
1040 let travel_decoded =
1042 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1043 let torque_decoded = i16::from_be_bytes([frame.data[4], frame.data[5]]);
1044 assert_eq!(travel_decoded, 50000);
1045 assert_eq!(torque_decoded, 2500);
1046 assert_eq!(frame.data[6] & 0x01, 0x01); assert_eq!(frame.data[7], 0x00); }
1049
1050 #[test]
1051 fn test_gripper_control_command_set_zero_point() {
1052 let cmd = GripperControlCommand::new(0.0, 0.0, false).set_zero_point();
1053 let frame = cmd.to_frame();
1054
1055 assert_eq!(frame.data[6], 0x00); assert_eq!(frame.data[7], 0xAE); }
1058
1059 #[test]
1060 fn test_gripper_control_command_clear_error() {
1061 let cmd = GripperControlCommand::new(50.0, 2.5, true).clear_error();
1062 let frame = cmd.to_frame();
1063
1064 assert_eq!(frame.data[6] & 0x03, 0x03); }
1066
1067 #[test]
1068 fn test_gripper_control_command_fully_closed() {
1069 let cmd = GripperControlCommand::new(0.0, 1.0, true);
1071 assert_eq!(cmd.travel_mm, 0);
1072 }
1073}
1074
1075#[derive(Debug, Clone, Copy, Default)]
1084pub struct EndPoseControl1 {
1085 pub x_mm: i32, pub y_mm: i32, }
1088
1089impl EndPoseControl1 {
1090 pub fn new(x: f64, y: f64) -> Self {
1092 Self {
1093 x_mm: (x * 1000.0) as i32,
1094 y_mm: (y * 1000.0) as i32,
1095 }
1096 }
1097
1098 pub fn to_frame(self) -> PiperFrame {
1100 let mut data = [0u8; 8];
1101 let x_bytes = i32_to_bytes_be(self.x_mm);
1102 let y_bytes = i32_to_bytes_be(self.y_mm);
1103 data[0..4].copy_from_slice(&x_bytes);
1104 data[4..8].copy_from_slice(&y_bytes);
1105
1106 PiperFrame::new_standard(ID_END_POSE_CONTROL_1 as u16, &data)
1107 }
1108}
1109
1110#[derive(Debug, Clone, Copy, Default)]
1116pub struct EndPoseControl2 {
1117 pub z_mm: i32, pub rx_deg: i32, }
1120
1121impl EndPoseControl2 {
1122 pub fn new(z: f64, rx: f64) -> Self {
1124 Self {
1125 z_mm: (z * 1000.0) as i32,
1126 rx_deg: (rx * 1000.0) as i32,
1127 }
1128 }
1129
1130 pub fn to_frame(self) -> PiperFrame {
1132 let mut data = [0u8; 8];
1133 let z_bytes = i32_to_bytes_be(self.z_mm);
1134 let rx_bytes = i32_to_bytes_be(self.rx_deg);
1135 data[0..4].copy_from_slice(&z_bytes);
1136 data[4..8].copy_from_slice(&rx_bytes);
1137
1138 PiperFrame::new_standard(ID_END_POSE_CONTROL_2 as u16, &data)
1139 }
1140}
1141
1142#[derive(Debug, Clone, Copy, Default)]
1147pub struct EndPoseControl3 {
1148 pub ry_deg: i32, pub rz_deg: i32, }
1151
1152impl EndPoseControl3 {
1153 pub fn new(ry: f64, rz: f64) -> Self {
1155 Self {
1156 ry_deg: (ry * 1000.0) as i32,
1157 rz_deg: (rz * 1000.0) as i32,
1158 }
1159 }
1160
1161 pub fn to_frame(self) -> PiperFrame {
1163 let mut data = [0u8; 8];
1164 let ry_bytes = i32_to_bytes_be(self.ry_deg);
1165 let rz_bytes = i32_to_bytes_be(self.rz_deg);
1166 data[0..4].copy_from_slice(&ry_bytes);
1167 data[4..8].copy_from_slice(&rz_bytes);
1168
1169 PiperFrame::new_standard(ID_END_POSE_CONTROL_3 as u16, &data)
1170 }
1171}
1172
1173#[cfg(test)]
1174mod end_pose_control_tests {
1175 use super::*;
1176
1177 #[test]
1178 fn test_end_pose_control1_new() {
1179 let cmd = EndPoseControl1::new(100.0, -50.0);
1180 assert_eq!(cmd.x_mm, 100000);
1181 assert_eq!(cmd.y_mm, -50000);
1182 }
1183
1184 #[test]
1185 fn test_end_pose_control1_to_frame() {
1186 let cmd = EndPoseControl1::new(100.0, -50.0);
1187 let frame = cmd.to_frame();
1188
1189 assert_eq!(frame.id, ID_END_POSE_CONTROL_1);
1190 let x_decoded =
1191 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1192 let y_decoded =
1193 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
1194 assert_eq!(x_decoded, 100000);
1195 assert_eq!(y_decoded, -50000);
1196 }
1197
1198 #[test]
1199 fn test_end_pose_control2_new() {
1200 let cmd = EndPoseControl2::new(200.0, 90.0);
1201 assert_eq!(cmd.z_mm, 200000);
1202 assert_eq!(cmd.rx_deg, 90000);
1203 }
1204
1205 #[test]
1206 fn test_end_pose_control2_to_frame() {
1207 let cmd = EndPoseControl2::new(200.0, 90.0);
1208 let frame = cmd.to_frame();
1209
1210 assert_eq!(frame.id, ID_END_POSE_CONTROL_2);
1211 let z_decoded =
1212 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1213 let rx_decoded =
1214 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
1215 assert_eq!(z_decoded, 200000);
1216 assert_eq!(rx_decoded, 90000);
1217 }
1218
1219 #[test]
1220 fn test_end_pose_control3_new() {
1221 let cmd = EndPoseControl3::new(-45.0, 180.0);
1222 assert_eq!(cmd.ry_deg, -45000);
1223 assert_eq!(cmd.rz_deg, 180000);
1224 }
1225
1226 #[test]
1227 fn test_end_pose_control3_to_frame() {
1228 let cmd = EndPoseControl3::new(-45.0, 180.0);
1229 let frame = cmd.to_frame();
1230
1231 assert_eq!(frame.id, ID_END_POSE_CONTROL_3);
1232 let ry_decoded =
1233 i32::from_be_bytes([frame.data[0], frame.data[1], frame.data[2], frame.data[3]]);
1234 let rz_decoded =
1235 i32::from_be_bytes([frame.data[4], frame.data[5], frame.data[6], frame.data[7]]);
1236 assert_eq!(ry_decoded, -45000);
1237 assert_eq!(rz_decoded, 180000);
1238 }
1239
1240 #[test]
1241 fn test_end_pose_control_precision() {
1242 let cmd = EndPoseControl1::new(0.5, -0.5);
1244 assert_eq!(cmd.x_mm, 500);
1245 assert_eq!(cmd.y_mm, -500);
1246 }
1247}
1248
1249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1255pub enum ArcPointIndex {
1256 Invalid = 0x00,
1258 Start = 0x01,
1260 Middle = 0x02,
1262 End = 0x03,
1264}
1265
1266impl TryFrom<u8> for ArcPointIndex {
1267 type Error = ProtocolError;
1268
1269 fn try_from(value: u8) -> Result<Self, Self::Error> {
1270 match value {
1271 0x00 => Ok(ArcPointIndex::Invalid),
1272 0x01 => Ok(ArcPointIndex::Start),
1273 0x02 => Ok(ArcPointIndex::Middle),
1274 0x03 => Ok(ArcPointIndex::End),
1275 _ => Err(ProtocolError::InvalidValue {
1276 field: "ArcPointIndex".to_string(),
1277 value,
1278 }),
1279 }
1280 }
1281}
1282
1283#[derive(Debug, Clone, Copy)]
1288pub struct ArcPointCommand {
1289 pub point_index: ArcPointIndex,
1290}
1291
1292impl ArcPointCommand {
1293 pub fn start() -> Self {
1295 Self {
1296 point_index: ArcPointIndex::Start,
1297 }
1298 }
1299
1300 pub fn middle() -> Self {
1302 Self {
1303 point_index: ArcPointIndex::Middle,
1304 }
1305 }
1306
1307 pub fn end() -> Self {
1309 Self {
1310 point_index: ArcPointIndex::End,
1311 }
1312 }
1313
1314 pub fn new(point_index: ArcPointIndex) -> Self {
1316 Self { point_index }
1317 }
1318
1319 pub fn to_frame(self) -> PiperFrame {
1321 let mut data = [0u8; 8];
1322 data[0] = self.point_index as u8;
1323 PiperFrame::new_standard(ID_ARC_POINT as u16, &data)
1326 }
1327}
1328
1329#[cfg(test)]
1330mod arc_point_tests {
1331 use super::*;
1332
1333 #[test]
1334 fn test_arc_point_index_from_u8() {
1335 assert_eq!(
1336 ArcPointIndex::try_from(0x00).unwrap(),
1337 ArcPointIndex::Invalid
1338 );
1339 assert_eq!(ArcPointIndex::try_from(0x01).unwrap(), ArcPointIndex::Start);
1340 assert_eq!(
1341 ArcPointIndex::try_from(0x02).unwrap(),
1342 ArcPointIndex::Middle
1343 );
1344 assert_eq!(ArcPointIndex::try_from(0x03).unwrap(), ArcPointIndex::End);
1345 }
1346
1347 #[test]
1348 fn test_arc_point_index_invalid() {
1349 assert!(ArcPointIndex::try_from(0x04).is_err());
1350 assert!(ArcPointIndex::try_from(0xFF).is_err());
1351 }
1352
1353 #[test]
1354 fn test_arc_point_command_start() {
1355 let cmd = ArcPointCommand::start();
1356 let frame = cmd.to_frame();
1357
1358 assert_eq!(frame.id, ID_ARC_POINT);
1359 assert_eq!(frame.data[0], 0x01);
1360 assert_eq!(frame.data[1], 0x00); }
1362
1363 #[test]
1364 fn test_arc_point_command_middle() {
1365 let cmd = ArcPointCommand::middle();
1366 let frame = cmd.to_frame();
1367
1368 assert_eq!(frame.id, ID_ARC_POINT);
1369 assert_eq!(frame.data[0], 0x02);
1370 }
1371
1372 #[test]
1373 fn test_arc_point_command_end() {
1374 let cmd = ArcPointCommand::end();
1375 let frame = cmd.to_frame();
1376
1377 assert_eq!(frame.id, ID_ARC_POINT);
1378 assert_eq!(frame.data[0], 0x03);
1379 }
1380}
1381
1382#[derive(Debug, Clone, Copy)]
1398pub struct MitControlCommand {
1399 pub joint_index: u8, pub pos_ref: f32, pub vel_ref: f32, pub kp: f32, pub kd: f32, pub t_ref: f32, }
1406
1407impl MitControlCommand {
1408 const P_MIN: f32 = -12.5;
1411 const P_MAX: f32 = 12.5;
1412 const V_MIN: f32 = -45.0;
1414 const V_MAX: f32 = 45.0;
1415 const KP_MIN: f32 = 0.0;
1417 const KP_MAX: f32 = 500.0;
1418 const KD_MIN: f32 = -5.0;
1420 const KD_MAX: f32 = 5.0;
1421 const T_MIN: f32 = -18.0;
1423 const T_MAX: f32 = 18.0;
1424
1425 fn float_to_uint(x: f32, x_min: f32, x_max: f32, bits: u32) -> u32 {
1429 let span = x_max - x_min;
1430 let offset = x_min;
1431 if span <= 0.0 {
1432 return 0;
1433 }
1434 let result = ((x - offset) * ((1u32 << bits) - 1) as f32 / span) as u32;
1435 result.min((1u32 << bits) - 1)
1436 }
1437
1438 #[allow(dead_code)]
1471 pub fn uint_to_float(x_int: u32, x_min: f32, x_max: f32, bits: u32) -> f32 {
1472 let span = x_max - x_min;
1473 let offset = x_min;
1474 (x_int as f32) * span / ((1u32 << bits) - 1) as f32 + offset
1475 }
1476
1477 pub fn new(joint_index: u8, pos_ref: f32, vel_ref: f32, kp: f32, kd: f32, t_ref: f32) -> Self {
1500 Self {
1501 joint_index,
1502 pos_ref,
1503 vel_ref,
1504 kp,
1505 kd,
1506 t_ref,
1507 }
1508 }
1509
1510 fn encode_to_bytes(&self) -> [u8; 8] {
1517 let mut data = [0u8; 8];
1518
1519 let pos_ref_uint = Self::float_to_uint(self.pos_ref, Self::P_MIN, Self::P_MAX, 16);
1521 data[0] = ((pos_ref_uint >> 8) & 0xFF) as u8;
1522 data[1] = (pos_ref_uint & 0xFF) as u8;
1523
1524 let vel_ref_uint = Self::float_to_uint(self.vel_ref, Self::V_MIN, Self::V_MAX, 12);
1526 data[2] = ((vel_ref_uint >> 4) & 0xFF) as u8; let kp_uint = Self::float_to_uint(self.kp, Self::KP_MIN, Self::KP_MAX, 12);
1529 let vel_ref_low = (vel_ref_uint & 0x0F) as u8;
1530 let kp_high = ((kp_uint >> 8) & 0x0F) as u8;
1531 data[3] = (vel_ref_low << 4) | kp_high;
1532
1533 data[4] = (kp_uint & 0xFF) as u8;
1535
1536 let kd_uint = Self::float_to_uint(self.kd, Self::KD_MIN, Self::KD_MAX, 12);
1538 data[5] = ((kd_uint >> 4) & 0xFF) as u8; let t_ref_uint = Self::float_to_uint(self.t_ref, Self::T_MIN, Self::T_MAX, 8);
1542
1543 let kd_low = (kd_uint & 0x0F) as u8;
1545 let t_ref_high = ((t_ref_uint >> 4) & 0x0F) as u8;
1546 data[6] = (kd_low << 4) | t_ref_high;
1547
1548 let t_ref_low = (t_ref_uint & 0x0F) as u8;
1550 data[7] = t_ref_low << 4; data
1553 }
1554
1555 fn calculate_crc(data: &[u8; 7], _joint_index: u8) -> u8 {
1568 let crc = data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4] ^ data[5] ^ data[6];
1569 crc & 0x0F
1570 }
1571
1572 pub fn to_frame(self) -> PiperFrame {
1601 let mut data = self.encode_to_bytes();
1603
1604 let crc = Self::calculate_crc(data[0..7].try_into().unwrap(), self.joint_index);
1608
1609 data[7] |= crc & 0x0F;
1612
1613 let can_id = ID_MIT_CONTROL_BASE + (self.joint_index - 1) as u32;
1614 PiperFrame::new_standard(can_id as u16, &data)
1615 }
1616
1617 #[cfg(test)]
1629 pub fn to_frame_with_custom_crc(self, custom_crc: u8) -> PiperFrame {
1630 let mut data = self.encode_to_bytes();
1632
1633 data[7] = (data[7] & 0xF0) | (custom_crc & 0x0F);
1635
1636 let can_id = ID_MIT_CONTROL_BASE + (self.joint_index - 1) as u32;
1637 PiperFrame::new_standard(can_id as u16, &data)
1638 }
1639}
1640
1641#[cfg(test)]
1642mod mit_control_tests {
1643 use super::*;
1644
1645 #[test]
1646 fn test_float_to_uint() {
1647 let result = MitControlCommand::float_to_uint(5.0, 0.0, 10.0, 12);
1649 assert_eq!(result, 2047);
1651 }
1652
1653 #[test]
1654 fn test_float_to_uint_boundary() {
1655 let min = MitControlCommand::float_to_uint(0.0, 0.0, 10.0, 12);
1657 let max = MitControlCommand::float_to_uint(10.0, 0.0, 10.0, 12);
1658 assert_eq!(min, 0);
1659 assert_eq!(max, 4095);
1660 }
1661
1662 #[test]
1663 fn test_uint_to_float() {
1664 let result = MitControlCommand::uint_to_float(2047, 0.0, 10.0, 12);
1666 assert!((result - 5.0).abs() < 0.01);
1668 }
1669
1670 #[test]
1671 fn test_uint_to_float_boundary() {
1672 let min = MitControlCommand::uint_to_float(0, 0.0, 10.0, 12);
1674 let max = MitControlCommand::uint_to_float(4095, 0.0, 10.0, 12);
1675 assert!((min - 0.0).abs() < 0.001);
1676 assert!((max - 10.0).abs() < 0.001);
1677 }
1678
1679 #[test]
1680 fn test_mit_control_command_new() {
1681 let cmd = MitControlCommand::new(1, 1.0, 2.0, 10.0, 0.8, 5.0);
1683 assert_eq!(cmd.joint_index, 1);
1684 assert_eq!(cmd.pos_ref, 1.0);
1685 assert_eq!(cmd.vel_ref, 2.0);
1686 assert_eq!(cmd.kp, 10.0);
1687 assert_eq!(cmd.kd, 0.8);
1688 assert_eq!(cmd.t_ref, 5.0);
1689 }
1690
1691 #[test]
1692 fn test_mit_control_command_calculate_crc() {
1693 let cmd = MitControlCommand::new(1, 0.0, 0.0, 0.0, 0.0, 0.0);
1699 let frame = cmd.to_frame();
1700 let crc = frame.data[7] & 0x0F;
1702 assert!(crc <= 0x0F, "CRC 应该在 0-15 范围内");
1703
1704 let cmd2 = MitControlCommand::new(1, 0.0, 0.0, 10.0, 0.8, 0.0);
1706 let frame2 = cmd2.to_frame();
1707 let crc2 = frame2.data[7] & 0x0F;
1708 assert!(crc2 <= 0x0F, "CRC 应该在 0-15 范围内");
1709 }
1710
1711 #[test]
1712 fn test_mit_control_command_roundtrip() {
1713 let original = 5.0f32;
1715 let x_min = 0.0f32;
1716 let x_max = 10.0f32;
1717 let bits = 12u32;
1718
1719 let uint_val = MitControlCommand::float_to_uint(original, x_min, x_max, bits);
1720 let float_val = MitControlCommand::uint_to_float(uint_val, x_min, x_max, bits);
1721
1722 assert!((float_val - original).abs() < 0.01);
1724 }
1725}
1726
1727#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, num_enum::FromPrimitive)]
1733#[repr(u8)]
1734pub enum LightControlEnable {
1735 #[default]
1737 Disabled = 0x00,
1738 Enabled = 0x01,
1740}
1741
1742#[derive(Debug, Clone, Copy)]
1746pub struct LightControlCommand {
1747 pub enable: LightControlEnable, pub joint_index: u8, pub led_index: u8, pub r: u8, pub g: u8, pub b: u8, pub counter: u8, }
1756
1757impl LightControlCommand {
1758 pub fn new(
1760 enable: LightControlEnable,
1761 joint_index: u8,
1762 led_index: u8,
1763 r: u8,
1764 g: u8,
1765 b: u8,
1766 counter: u8,
1767 ) -> Self {
1768 Self {
1769 enable,
1770 joint_index,
1771 led_index,
1772 r,
1773 g,
1774 b,
1775 counter,
1776 }
1777 }
1778
1779 pub fn to_frame(self) -> PiperFrame {
1781 let mut data = [0u8; 8];
1782 data[0] = self.enable as u8;
1783 data[1] = self.joint_index;
1784 data[2] = self.led_index;
1785 data[3] = self.r;
1786 data[4] = self.g;
1787 data[5] = self.b;
1788 data[7] = self.counter;
1790
1791 PiperFrame::new_standard(ID_LIGHT_CONTROL as u16, &data)
1792 }
1793}
1794
1795#[cfg(test)]
1796mod light_control_tests {
1797 use super::*;
1798
1799 #[test]
1800 fn test_light_control_enable_from() {
1801 assert_eq!(LightControlEnable::from(0x00), LightControlEnable::Disabled);
1802 assert_eq!(LightControlEnable::from(0x01), LightControlEnable::Enabled);
1803 assert_eq!(LightControlEnable::from(0xFF), LightControlEnable::Disabled);
1804 }
1806
1807 #[test]
1808 fn test_light_control_command_new() {
1809 let cmd = LightControlCommand::new(LightControlEnable::Enabled, 1, 0xFF, 255, 128, 0, 10);
1810 assert_eq!(cmd.enable, LightControlEnable::Enabled);
1811 assert_eq!(cmd.joint_index, 1);
1812 assert_eq!(cmd.led_index, 0xFF);
1813 assert_eq!(cmd.r, 255);
1814 assert_eq!(cmd.g, 128);
1815 assert_eq!(cmd.b, 0);
1816 assert_eq!(cmd.counter, 10);
1817 }
1818
1819 #[test]
1820 fn test_light_control_command_to_frame() {
1821 let cmd = LightControlCommand::new(LightControlEnable::Enabled, 2, 5, 100, 200, 50, 42);
1822 let frame = cmd.to_frame();
1823
1824 assert_eq!(frame.id, ID_LIGHT_CONTROL);
1825 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); }
1834
1835 #[test]
1836 fn test_light_control_command_all_leds() {
1837 let cmd = LightControlCommand::new(LightControlEnable::Enabled, 3, 0xFF, 255, 255, 255, 0);
1839 let frame = cmd.to_frame();
1840 assert_eq!(frame.data[2], 0xFF);
1841 }
1842
1843 #[test]
1844 fn test_light_control_command_disabled() {
1845 let cmd = LightControlCommand::new(LightControlEnable::Disabled, 1, 0, 0, 0, 0, 0);
1846 let frame = cmd.to_frame();
1847 assert_eq!(frame.data[0], 0x00);
1848 }
1849}