1use crate::can::PiperFrame;
7use crate::control::{ControlModeCommand, InstallPosition, MitMode};
8use crate::{
9 ProtocolError, bytes_to_i16_be, bytes_to_i32_be,
10 ids::{
11 ID_CONTROL_MODE, ID_END_POSE_1, ID_END_POSE_2, ID_END_POSE_3, ID_FIRMWARE_READ,
12 ID_GRIPPER_CONTROL, ID_GRIPPER_FEEDBACK, ID_JOINT_CONTROL_12, ID_JOINT_CONTROL_34,
13 ID_JOINT_CONTROL_56, ID_JOINT_DRIVER_HIGH_SPEED_BASE, ID_JOINT_DRIVER_LOW_SPEED_BASE,
14 ID_JOINT_END_VELOCITY_ACCEL_BASE, ID_JOINT_FEEDBACK_12, ID_JOINT_FEEDBACK_34,
15 ID_JOINT_FEEDBACK_56, ID_ROBOT_STATUS,
16 },
17};
18use bilge::prelude::*;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, num_enum::FromPrimitive)]
29#[repr(u8)]
30pub enum ControlMode {
31 #[default]
33 Standby = 0x00,
34 CanControl = 0x01,
36 Teach = 0x02,
38 Ethernet = 0x03,
40 Wifi = 0x04,
42 Remote = 0x05,
44 LinkTeach = 0x06,
46 OfflineTrajectory = 0x07,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, num_enum::FromPrimitive)]
52#[repr(u8)]
53pub enum RobotStatus {
54 #[default]
56 Normal = 0x00,
57 EmergencyStop = 0x01,
59 NoSolution = 0x02,
61 Singularity = 0x03,
63 AngleLimitExceeded = 0x04,
65 JointCommError = 0x05,
67 JointBrakeNotOpen = 0x06,
69 Collision = 0x07,
71 TeachOverspeed = 0x08,
73 JointStatusError = 0x09,
75 OtherError = 0x0A,
77 TeachRecord = 0x0B,
79 TeachExecute = 0x0C,
81 TeachPause = 0x0D,
83 MainControlOverTemp = 0x0E,
85 ResistorOverTemp = 0x0F,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, num_enum::FromPrimitive)]
91#[repr(u8)]
92pub enum MoveMode {
93 #[default]
95 MoveP = 0x00,
96 MoveJ = 0x01,
98 MoveL = 0x02,
100 MoveC = 0x03,
102 MoveM = 0x04,
104 MoveCpv = 0x05,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, num_enum::FromPrimitive)]
110#[repr(u8)]
111pub enum TeachStatus {
112 #[default]
114 Closed = 0x00,
115 StartRecord = 0x01,
117 EndRecord = 0x02,
119 Execute = 0x03,
121 Pause = 0x04,
123 Continue = 0x05,
125 Terminate = 0x06,
127 MoveToStart = 0x07,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, num_enum::FromPrimitive)]
133#[repr(u8)]
134pub enum MotionStatus {
135 #[default]
137 Arrived = 0x00,
138 NotArrived = 0x01,
140}
141
142#[bitsize(8)]
161#[derive(FromBits, DebugBits, Clone, Copy, Default)]
162pub struct FaultCodeAngleLimit {
163 pub joint1_limit: bool, pub joint2_limit: bool, pub joint3_limit: bool, pub joint4_limit: bool, pub joint5_limit: bool, pub joint6_limit: bool, pub reserved: u2, }
171
172#[bitsize(8)]
183#[derive(FromBits, DebugBits, Clone, Copy, Default)]
184pub struct FaultCodeCommError {
185 pub joint1_comm_error: bool, pub joint2_comm_error: bool, pub joint3_comm_error: bool, pub joint4_comm_error: bool, pub joint5_comm_error: bool, pub joint6_comm_error: bool, pub reserved: u2, }
193
194#[derive(Debug, Clone, Copy, Default)]
203pub struct RobotStatusFeedback {
204 pub control_mode: ControlMode, pub robot_status: RobotStatus, pub move_mode: MoveMode, pub teach_status: TeachStatus, pub motion_status: MotionStatus, pub trajectory_point_index: u8, pub fault_code_angle_limit: FaultCodeAngleLimit, pub fault_code_comm_error: FaultCodeCommError, }
213
214impl TryFrom<PiperFrame> for RobotStatusFeedback {
215 type Error = ProtocolError;
216
217 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
218 if frame.id != ID_ROBOT_STATUS {
220 return Err(ProtocolError::InvalidCanId { id: frame.id });
221 }
222
223 if frame.len < 8 {
225 return Err(ProtocolError::InvalidLength {
226 expected: 8,
227 actual: frame.len as usize,
228 });
229 }
230
231 Ok(Self {
233 control_mode: ControlMode::from(frame.data[0]),
234 robot_status: RobotStatus::from(frame.data[1]),
235 move_mode: MoveMode::from(frame.data[2]),
236 teach_status: TeachStatus::from(frame.data[3]),
237 motion_status: MotionStatus::from(frame.data[4]),
238 trajectory_point_index: frame.data[5],
239 fault_code_angle_limit: FaultCodeAngleLimit::from(u8::new(frame.data[6])),
240 fault_code_comm_error: FaultCodeCommError::from(u8::new(frame.data[7])),
241 })
242 }
243}
244
245#[derive(Debug, Clone, Copy, Default)]
254pub struct JointFeedback12 {
255 pub j1_deg: i32, pub j2_deg: i32, }
258
259impl JointFeedback12 {
260 pub fn j1_raw(&self) -> i32 {
262 self.j1_deg
263 }
264
265 pub fn j2_raw(&self) -> i32 {
267 self.j2_deg
268 }
269
270 pub fn j1(&self) -> f64 {
272 self.j1_deg as f64 / 1000.0
273 }
274
275 pub fn j2(&self) -> f64 {
277 self.j2_deg as f64 / 1000.0
278 }
279
280 pub fn j1_rad(&self) -> f64 {
282 self.j1() * std::f64::consts::PI / 180.0
283 }
284
285 pub fn j2_rad(&self) -> f64 {
287 self.j2() * std::f64::consts::PI / 180.0
288 }
289}
290
291impl TryFrom<PiperFrame> for JointFeedback12 {
292 type Error = ProtocolError;
293
294 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
295 if frame.id != ID_JOINT_FEEDBACK_12 {
297 return Err(ProtocolError::InvalidCanId { id: frame.id });
298 }
299
300 if frame.len < 8 {
302 return Err(ProtocolError::InvalidLength {
303 expected: 8,
304 actual: frame.len as usize,
305 });
306 }
307
308 let j1_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
310 let j1_deg = bytes_to_i32_be(j1_bytes);
311
312 let j2_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
314 let j2_deg = bytes_to_i32_be(j2_bytes);
315
316 Ok(Self { j1_deg, j2_deg })
317 }
318}
319
320#[derive(Debug, Clone, Copy, Default)]
325pub struct JointFeedback34 {
326 pub j3_deg: i32, pub j4_deg: i32, }
329
330impl JointFeedback34 {
331 pub fn j3_raw(&self) -> i32 {
333 self.j3_deg
334 }
335
336 pub fn j4_raw(&self) -> i32 {
338 self.j4_deg
339 }
340
341 pub fn j3(&self) -> f64 {
343 self.j3_deg as f64 / 1000.0
344 }
345
346 pub fn j4(&self) -> f64 {
348 self.j4_deg as f64 / 1000.0
349 }
350
351 pub fn j3_rad(&self) -> f64 {
353 self.j3() * std::f64::consts::PI / 180.0
354 }
355
356 pub fn j4_rad(&self) -> f64 {
358 self.j4() * std::f64::consts::PI / 180.0
359 }
360}
361
362impl TryFrom<PiperFrame> for JointFeedback34 {
363 type Error = ProtocolError;
364
365 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
366 if frame.id != ID_JOINT_FEEDBACK_34 {
368 return Err(ProtocolError::InvalidCanId { id: frame.id });
369 }
370
371 if frame.len < 8 {
373 return Err(ProtocolError::InvalidLength {
374 expected: 8,
375 actual: frame.len as usize,
376 });
377 }
378
379 let j3_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
381 let j3_deg = bytes_to_i32_be(j3_bytes);
382
383 let j4_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
385 let j4_deg = bytes_to_i32_be(j4_bytes);
386
387 Ok(Self { j3_deg, j4_deg })
388 }
389}
390
391#[derive(Debug, Clone, Copy, Default)]
396pub struct JointFeedback56 {
397 pub j5_deg: i32, pub j6_deg: i32, }
400
401impl JointFeedback56 {
402 pub fn j5_raw(&self) -> i32 {
404 self.j5_deg
405 }
406
407 pub fn j6_raw(&self) -> i32 {
409 self.j6_deg
410 }
411
412 pub fn j5(&self) -> f64 {
414 self.j5_deg as f64 / 1000.0
415 }
416
417 pub fn j6(&self) -> f64 {
419 self.j6_deg as f64 / 1000.0
420 }
421
422 pub fn j5_rad(&self) -> f64 {
424 self.j5() * std::f64::consts::PI / 180.0
425 }
426
427 pub fn j6_rad(&self) -> f64 {
429 self.j6() * std::f64::consts::PI / 180.0
430 }
431}
432
433impl TryFrom<PiperFrame> for JointFeedback56 {
434 type Error = ProtocolError;
435
436 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
437 if frame.id != ID_JOINT_FEEDBACK_56 {
439 return Err(ProtocolError::InvalidCanId { id: frame.id });
440 }
441
442 if frame.len < 8 {
444 return Err(ProtocolError::InvalidLength {
445 expected: 8,
446 actual: frame.len as usize,
447 });
448 }
449
450 let j5_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
452 let j5_deg = bytes_to_i32_be(j5_bytes);
453
454 let j6_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
456 let j6_deg = bytes_to_i32_be(j6_bytes);
457
458 Ok(Self { j5_deg, j6_deg })
459 }
460}
461
462#[derive(Debug, Clone, Copy, Default)]
471pub struct EndPoseFeedback1 {
472 pub x_mm: i32, pub y_mm: i32, }
475
476impl EndPoseFeedback1 {
477 pub fn x_raw(&self) -> i32 {
479 self.x_mm
480 }
481
482 pub fn y_raw(&self) -> i32 {
484 self.y_mm
485 }
486
487 pub fn x(&self) -> f64 {
489 self.x_mm as f64 / 1000.0
490 }
491
492 pub fn y(&self) -> f64 {
494 self.y_mm as f64 / 1000.0
495 }
496}
497
498impl TryFrom<PiperFrame> for EndPoseFeedback1 {
499 type Error = ProtocolError;
500
501 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
502 if frame.id != ID_END_POSE_1 {
504 return Err(ProtocolError::InvalidCanId { id: frame.id });
505 }
506
507 if frame.len < 8 {
509 return Err(ProtocolError::InvalidLength {
510 expected: 8,
511 actual: frame.len as usize,
512 });
513 }
514
515 let x_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
517 let x_mm = bytes_to_i32_be(x_bytes);
518
519 let y_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
521 let y_mm = bytes_to_i32_be(y_bytes);
522
523 Ok(Self { x_mm, y_mm })
524 }
525}
526
527#[derive(Debug, Clone, Copy, Default)]
532pub struct EndPoseFeedback2 {
533 pub z_mm: i32, pub rx_deg: i32, }
536
537impl EndPoseFeedback2 {
538 pub fn z_raw(&self) -> i32 {
540 self.z_mm
541 }
542
543 pub fn rx_raw(&self) -> i32 {
545 self.rx_deg
546 }
547
548 pub fn z(&self) -> f64 {
550 self.z_mm as f64 / 1000.0
551 }
552
553 pub fn rx(&self) -> f64 {
555 self.rx_deg as f64 / 1000.0
556 }
557
558 pub fn rx_rad(&self) -> f64 {
560 self.rx() * std::f64::consts::PI / 180.0
561 }
562}
563
564impl TryFrom<PiperFrame> for EndPoseFeedback2 {
565 type Error = ProtocolError;
566
567 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
568 if frame.id != ID_END_POSE_2 {
570 return Err(ProtocolError::InvalidCanId { id: frame.id });
571 }
572
573 if frame.len < 8 {
575 return Err(ProtocolError::InvalidLength {
576 expected: 8,
577 actual: frame.len as usize,
578 });
579 }
580
581 let z_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
583 let z_mm = bytes_to_i32_be(z_bytes);
584
585 let rx_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
587 let rx_deg = bytes_to_i32_be(rx_bytes);
588
589 Ok(Self { z_mm, rx_deg })
590 }
591}
592
593#[derive(Debug, Clone, Copy, Default)]
598pub struct EndPoseFeedback3 {
599 pub ry_deg: i32, pub rz_deg: i32, }
602
603impl EndPoseFeedback3 {
604 pub fn ry_raw(&self) -> i32 {
606 self.ry_deg
607 }
608
609 pub fn rz_raw(&self) -> i32 {
611 self.rz_deg
612 }
613
614 pub fn ry(&self) -> f64 {
616 self.ry_deg as f64 / 1000.0
617 }
618
619 pub fn rz(&self) -> f64 {
621 self.rz_deg as f64 / 1000.0
622 }
623
624 pub fn ry_rad(&self) -> f64 {
626 self.ry() * std::f64::consts::PI / 180.0
627 }
628
629 pub fn rz_rad(&self) -> f64 {
631 self.rz() * std::f64::consts::PI / 180.0
632 }
633}
634
635impl TryFrom<PiperFrame> for EndPoseFeedback3 {
636 type Error = ProtocolError;
637
638 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
639 if frame.id != ID_END_POSE_3 {
641 return Err(ProtocolError::InvalidCanId { id: frame.id });
642 }
643
644 if frame.len < 8 {
646 return Err(ProtocolError::InvalidLength {
647 expected: 8,
648 actual: frame.len as usize,
649 });
650 }
651
652 let ry_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
654 let ry_deg = bytes_to_i32_be(ry_bytes);
655
656 let rz_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
658 let rz_deg = bytes_to_i32_be(rz_bytes);
659
660 Ok(Self { ry_deg, rz_deg })
661 }
662}
663
664#[derive(Debug, Clone, Copy, Default)]
677pub struct JointDriverHighSpeedFeedback {
678 pub joint_index: u8, pub speed_rad_s: i16, pub current_a: i16, pub position_rad: i32, }
688
689impl JointDriverHighSpeedFeedback {
690 pub const COEFFICIENT_1_3: f64 = 1.18125;
695
696 pub const COEFFICIENT_4_6: f64 = 0.95844;
701
702 pub fn speed_raw(&self) -> i16 {
704 self.speed_rad_s
705 }
706
707 pub fn current_raw(&self) -> i16 {
709 self.current_a
710 }
711
712 pub fn position_raw(&self) -> i32 {
714 self.position_rad
715 }
716
717 pub fn speed(&self) -> f64 {
719 self.speed_rad_s as f64 / 1000.0
720 }
721
722 pub fn current(&self) -> f64 {
726 self.current_a as f64 / 1000.0
727 }
728
729 #[deprecated(
747 since = "0.1.0",
748 note = "Field unit unverified (rad vs mrad). Prefer `Observer::get_joint_position()` for verified position data, or use `position_raw()` for raw access."
749 )]
750 pub fn position(&self) -> f64 {
751 self.position_rad as f64
752 }
753
754 pub fn torque(&self, current_opt: Option<f64>) -> f64 {
781 let current = current_opt.unwrap_or_else(|| self.current());
782 let coefficient = if self.joint_index <= 3 {
783 Self::COEFFICIENT_1_3
784 } else {
785 Self::COEFFICIENT_4_6
786 };
787 current * coefficient
788 }
789
790 pub fn torque_raw(&self) -> i32 {
807 (self.torque(None) * 1000.0).round() as i32
808 }
809}
810
811impl TryFrom<PiperFrame> for JointDriverHighSpeedFeedback {
812 type Error = ProtocolError;
813
814 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
815 if frame.id < ID_JOINT_DRIVER_HIGH_SPEED_BASE
817 || frame.id > ID_JOINT_DRIVER_HIGH_SPEED_BASE + 5
818 {
819 return Err(ProtocolError::InvalidCanId { id: frame.id });
820 }
821
822 let joint_index = (frame.id - ID_JOINT_DRIVER_HIGH_SPEED_BASE + 1) as u8;
824
825 if frame.len < 8 {
827 return Err(ProtocolError::InvalidLength {
828 expected: 8,
829 actual: frame.len as usize,
830 });
831 }
832
833 let speed_bytes = [frame.data[0], frame.data[1]];
835 let speed_rad_s = bytes_to_i16_be(speed_bytes);
836
837 let current_bytes = [frame.data[2], frame.data[3]];
839 let current_a = bytes_to_i16_be(current_bytes);
840
841 let position_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
843 let position_rad = bytes_to_i32_be(position_bytes);
844
845 Ok(Self {
846 joint_index,
847 speed_rad_s,
848 current_a,
849 position_rad,
850 })
851 }
852}
853
854#[bitsize(8)]
870#[derive(FromBits, DebugBits, Clone, Copy, Default)]
871pub struct DriverStatus {
872 pub voltage_low: bool, pub motor_over_temp: bool, pub driver_over_current: bool, pub driver_over_temp: bool, pub collision_protection: bool, pub driver_error: bool, pub enabled: bool, pub stall_protection: bool, }
881
882#[derive(Debug, Clone, Copy, Default)]
889pub struct JointDriverLowSpeedFeedback {
890 pub joint_index: u8, pub voltage: u16, pub driver_temp: i16, pub motor_temp: i8, pub status: DriverStatus, pub bus_current: u16, }
897
898impl JointDriverLowSpeedFeedback {
899 pub fn voltage_raw(&self) -> u16 {
901 self.voltage
902 }
903
904 pub fn driver_temp_raw(&self) -> i16 {
906 self.driver_temp
907 }
908
909 pub fn motor_temp_raw(&self) -> i8 {
911 self.motor_temp
912 }
913
914 pub fn bus_current_raw(&self) -> u16 {
916 self.bus_current
917 }
918
919 pub fn voltage(&self) -> f64 {
921 self.voltage as f64 / 10.0
922 }
923
924 pub fn driver_temp(&self) -> f64 {
926 self.driver_temp as f64
927 }
928
929 pub fn motor_temp(&self) -> f64 {
931 self.motor_temp as f64
932 }
933
934 pub fn bus_current(&self) -> f64 {
936 self.bus_current as f64 / 1000.0
937 }
938}
939
940impl TryFrom<PiperFrame> for JointDriverLowSpeedFeedback {
941 type Error = ProtocolError;
942
943 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
944 let joint_index = (frame.id - ID_JOINT_DRIVER_LOW_SPEED_BASE + 1) as u8;
946 if !(1..=6).contains(&joint_index) {
947 return Err(ProtocolError::InvalidCanId { id: frame.id });
948 }
949
950 if frame.len < 8 {
952 return Err(ProtocolError::InvalidLength {
953 expected: 8,
954 actual: frame.len as usize,
955 });
956 }
957
958 let voltage_bytes = [frame.data[0], frame.data[1]];
960 let voltage = u16::from_be_bytes(voltage_bytes);
961
962 let driver_temp_bytes = [frame.data[2], frame.data[3]];
963 let driver_temp = bytes_to_i16_be(driver_temp_bytes);
964
965 let motor_temp = frame.data[4] as i8;
966
967 let status = DriverStatus::from(u8::new(frame.data[5]));
969
970 let bus_current_bytes = [frame.data[6], frame.data[7]];
971 let bus_current = u16::from_be_bytes(bus_current_bytes);
972
973 Ok(Self {
974 joint_index,
975 voltage,
976 driver_temp,
977 motor_temp,
978 status,
979 bus_current,
980 })
981 }
982}
983
984#[derive(Debug, Clone, Copy, Default)]
998pub struct JointEndVelocityAccelFeedback {
999 pub joint_index: u8, pub linear_velocity_m_s_raw: u16, pub angular_velocity_rad_s_raw: u16, pub linear_accel_m_s2_raw: u16, pub angular_accel_rad_s2_raw: u16, }
1005
1006impl JointEndVelocityAccelFeedback {
1007 pub fn linear_velocity_raw(&self) -> u16 {
1009 self.linear_velocity_m_s_raw
1010 }
1011
1012 pub fn angular_velocity_raw(&self) -> u16 {
1014 self.angular_velocity_rad_s_raw
1015 }
1016
1017 pub fn linear_accel_raw(&self) -> u16 {
1019 self.linear_accel_m_s2_raw
1020 }
1021
1022 pub fn angular_accel_raw(&self) -> u16 {
1024 self.angular_accel_rad_s2_raw
1025 }
1026
1027 pub fn linear_velocity(&self) -> f64 {
1029 self.linear_velocity_m_s_raw as f64 / 1000.0
1030 }
1031
1032 pub fn angular_velocity(&self) -> f64 {
1034 self.angular_velocity_rad_s_raw as f64 / 1000.0
1035 }
1036
1037 pub fn linear_accel(&self) -> f64 {
1039 self.linear_accel_m_s2_raw as f64 / 1000.0
1040 }
1041
1042 pub fn angular_accel(&self) -> f64 {
1044 self.angular_accel_rad_s2_raw as f64 / 1000.0
1045 }
1046}
1047
1048impl TryFrom<PiperFrame> for JointEndVelocityAccelFeedback {
1049 type Error = ProtocolError;
1050
1051 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
1052 if frame.id < ID_JOINT_END_VELOCITY_ACCEL_BASE
1054 || frame.id > ID_JOINT_END_VELOCITY_ACCEL_BASE + 5
1055 {
1056 return Err(ProtocolError::InvalidCanId { id: frame.id });
1057 }
1058
1059 let joint_index = (frame.id - ID_JOINT_END_VELOCITY_ACCEL_BASE + 1) as u8;
1061
1062 if frame.len < 8 {
1064 return Err(ProtocolError::InvalidLength {
1065 expected: 8,
1066 actual: frame.len as usize,
1067 });
1068 }
1069
1070 let linear_velocity_bytes = [frame.data[0], frame.data[1]];
1072 let linear_velocity_m_s_raw = u16::from_be_bytes(linear_velocity_bytes);
1073
1074 let angular_velocity_bytes = [frame.data[2], frame.data[3]];
1075 let angular_velocity_rad_s_raw = u16::from_be_bytes(angular_velocity_bytes);
1076
1077 let linear_accel_bytes = [frame.data[4], frame.data[5]];
1078 let linear_accel_m_s2_raw = u16::from_be_bytes(linear_accel_bytes);
1079
1080 let angular_accel_bytes = [frame.data[6], frame.data[7]];
1081 let angular_accel_rad_s2_raw = u16::from_be_bytes(angular_accel_bytes);
1082
1083 Ok(Self {
1084 joint_index,
1085 linear_velocity_m_s_raw,
1086 angular_velocity_rad_s_raw,
1087 linear_accel_m_s2_raw,
1088 angular_accel_rad_s2_raw,
1089 })
1090 }
1091}
1092
1093#[bitsize(8)]
1109#[derive(FromBits, DebugBits, Clone, Copy, Default)]
1110pub struct GripperStatus {
1111 pub voltage_low: bool, pub motor_over_temp: bool, pub driver_over_current: bool, pub driver_over_temp: bool, pub sensor_error: bool, pub driver_error: bool, pub enabled: bool, pub homed: bool, }
1120
1121#[derive(Debug, Clone, Copy, Default)]
1127pub struct GripperFeedback {
1128 pub travel_mm: i32, pub torque_nm: i16, pub status: GripperStatus, }
1133
1134impl GripperFeedback {
1135 pub fn travel_raw(&self) -> i32 {
1137 self.travel_mm
1138 }
1139
1140 pub fn torque_raw(&self) -> i16 {
1142 self.torque_nm
1143 }
1144
1145 pub fn travel(&self) -> f64 {
1147 self.travel_mm as f64 / 1000.0
1148 }
1149
1150 pub fn torque(&self) -> f64 {
1152 self.torque_nm as f64 / 1000.0
1153 }
1154}
1155
1156impl TryFrom<PiperFrame> for GripperFeedback {
1157 type Error = ProtocolError;
1158
1159 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
1160 if frame.id != ID_GRIPPER_FEEDBACK {
1162 return Err(ProtocolError::InvalidCanId { id: frame.id });
1163 }
1164
1165 if frame.len < 7 {
1167 return Err(ProtocolError::InvalidLength {
1168 expected: 7,
1169 actual: frame.len as usize,
1170 });
1171 }
1172
1173 let travel_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
1175 let travel_mm = bytes_to_i32_be(travel_bytes);
1176
1177 let torque_bytes = [frame.data[4], frame.data[5]];
1178 let torque_nm = bytes_to_i16_be(torque_bytes);
1179
1180 let status = GripperStatus::from(u8::new(frame.data[6]));
1182
1183 Ok(Self {
1184 travel_mm,
1185 torque_nm,
1186 status,
1187 })
1188 }
1189}
1190
1191#[cfg(test)]
1192mod tests {
1193 use super::*;
1194
1195 #[test]
1196 fn test_control_mode_from_u8() {
1197 assert_eq!(ControlMode::from(0x00), ControlMode::Standby);
1198 assert_eq!(ControlMode::from(0x01), ControlMode::CanControl);
1199 assert_eq!(ControlMode::from(0x02), ControlMode::Teach);
1200 assert_eq!(ControlMode::from(0x03), ControlMode::Ethernet);
1201 assert_eq!(ControlMode::from(0x04), ControlMode::Wifi);
1202 assert_eq!(ControlMode::from(0x05), ControlMode::Remote);
1203 assert_eq!(ControlMode::from(0x06), ControlMode::LinkTeach);
1204 assert_eq!(ControlMode::from(0x07), ControlMode::OfflineTrajectory);
1205 assert_eq!(ControlMode::from(0xFF), ControlMode::Standby);
1207 }
1208
1209 #[test]
1210 fn test_robot_status_from_u8() {
1211 assert_eq!(RobotStatus::from(0x00), RobotStatus::Normal);
1212 assert_eq!(RobotStatus::from(0x01), RobotStatus::EmergencyStop);
1213 assert_eq!(RobotStatus::from(0x0F), RobotStatus::ResistorOverTemp);
1214 for i in 0x00..=0x0F {
1216 let status = RobotStatus::from(i);
1217 assert_eq!(status as u8, i);
1218 }
1219 }
1220
1221 #[test]
1222 fn test_move_mode_from_u8() {
1223 assert_eq!(MoveMode::from(0x00), MoveMode::MoveP);
1224 assert_eq!(MoveMode::from(0x01), MoveMode::MoveJ);
1225 assert_eq!(MoveMode::from(0x02), MoveMode::MoveL);
1226 assert_eq!(MoveMode::from(0x03), MoveMode::MoveC);
1227 assert_eq!(MoveMode::from(0x04), MoveMode::MoveM);
1228 assert_eq!(MoveMode::from(0x05), MoveMode::MoveCpv);
1229 assert_eq!(MoveMode::from(0xFF), MoveMode::MoveP);
1231 }
1232
1233 #[test]
1234 fn test_move_mode_cpv() {
1235 assert_eq!(MoveMode::from(0x05), MoveMode::MoveCpv);
1236 assert_eq!(MoveMode::MoveCpv as u8, 0x05);
1237 }
1238
1239 #[test]
1240 fn test_move_mode_all_values() {
1241 for (value, expected) in [
1243 (0x00, MoveMode::MoveP),
1244 (0x01, MoveMode::MoveJ),
1245 (0x02, MoveMode::MoveL),
1246 (0x03, MoveMode::MoveC),
1247 (0x04, MoveMode::MoveM),
1248 (0x05, MoveMode::MoveCpv),
1249 ] {
1250 assert_eq!(MoveMode::from(value), expected);
1251 assert_eq!(expected as u8, value);
1252 }
1253 }
1254
1255 #[test]
1256 fn test_teach_status_from_u8() {
1257 assert_eq!(TeachStatus::from(0x00), TeachStatus::Closed);
1258 assert_eq!(TeachStatus::from(0x01), TeachStatus::StartRecord);
1259 assert_eq!(TeachStatus::from(0x07), TeachStatus::MoveToStart);
1260 for i in 0x00..=0x07 {
1262 let status = TeachStatus::from(i);
1263 assert_eq!(status as u8, i);
1264 }
1265 }
1266
1267 #[test]
1268 fn test_motion_status_from_u8() {
1269 assert_eq!(MotionStatus::from(0x00), MotionStatus::Arrived);
1270 assert_eq!(MotionStatus::from(0x01), MotionStatus::NotArrived);
1271 assert_eq!(MotionStatus::from(0xFF), MotionStatus::Arrived);
1273 }
1274
1275 #[test]
1276 fn test_enum_values_match_protocol() {
1277 assert_eq!(ControlMode::Standby as u8, 0x00);
1279 assert_eq!(ControlMode::CanControl as u8, 0x01);
1280 assert_eq!(ControlMode::Remote as u8, 0x05);
1281 assert_eq!(ControlMode::LinkTeach as u8, 0x06);
1282
1283 assert_eq!(RobotStatus::Normal as u8, 0x00);
1284 assert_eq!(RobotStatus::EmergencyStop as u8, 0x01);
1285 assert_eq!(RobotStatus::ResistorOverTemp as u8, 0x0F);
1286
1287 assert_eq!(MoveMode::MoveP as u8, 0x00);
1288 assert_eq!(MoveMode::MoveM as u8, 0x04);
1289 assert_eq!(MoveMode::MoveCpv as u8, 0x05);
1290 }
1291
1292 #[test]
1310 fn test_fault_code_angle_limit_bit_order() {
1311 let byte = 0x01;
1315 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1316 assert!(fault.joint1_limit(), "1号关节应该超限位");
1317 assert!(!fault.joint2_limit(), "2号关节应该正常");
1318 assert!(!fault.joint3_limit(), "3号关节应该正常");
1319 assert!(!fault.joint4_limit(), "4号关节应该正常");
1320 assert!(!fault.joint5_limit(), "5号关节应该正常");
1321 assert!(!fault.joint6_limit(), "6号关节应该正常");
1322
1323 let byte = 0x02;
1327 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1328 assert!(!fault.joint1_limit(), "1号关节应该正常");
1329 assert!(fault.joint2_limit(), "2号关节应该超限位");
1330 assert!(!fault.joint3_limit(), "3号关节应该正常");
1331
1332 let byte = 0x03;
1336 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1337 assert!(fault.joint1_limit(), "1号关节应该超限位");
1338 assert!(fault.joint2_limit(), "2号关节应该超限位");
1339 assert!(!fault.joint3_limit(), "3号关节应该正常");
1340
1341 let byte = 0x3F;
1344 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1345 assert!(fault.joint1_limit(), "1号关节应该超限位");
1346 assert!(fault.joint2_limit(), "2号关节应该超限位");
1347 assert!(fault.joint3_limit(), "3号关节应该超限位");
1348 assert!(fault.joint4_limit(), "4号关节应该超限位");
1349 assert!(fault.joint5_limit(), "5号关节应该超限位");
1350 assert!(fault.joint6_limit(), "6号关节应该超限位");
1351 }
1352
1353 #[test]
1354 fn test_fault_code_angle_limit_encode() {
1355 let mut fault = FaultCodeAngleLimit::from(u8::new(0));
1359 fault.set_joint1_limit(true);
1360 fault.set_joint3_limit(true);
1361 fault.set_joint5_limit(true);
1362
1363 let encoded = u8::from(fault).value();
1365 assert_eq!(encoded, 0x15, "编码值应该是 0x15 (Bit 0, 2, 4 = 1)");
1366
1367 assert!(fault.joint1_limit());
1369 assert!(!fault.joint2_limit());
1370 assert!(fault.joint3_limit());
1371 assert!(!fault.joint4_limit());
1372 assert!(fault.joint5_limit());
1373 assert!(!fault.joint6_limit());
1374 }
1375
1376 #[test]
1377 fn test_fault_code_angle_limit_roundtrip() {
1378 let original_byte = 0x3C;
1382 let fault = FaultCodeAngleLimit::from(u8::new(original_byte));
1383 let encoded = u8::from(fault).value();
1384
1385 assert!(!fault.joint1_limit());
1387 assert!(!fault.joint2_limit());
1388 assert!(fault.joint3_limit());
1389 assert!(fault.joint4_limit());
1390 assert!(fault.joint5_limit());
1391 assert!(fault.joint6_limit());
1392
1393 assert_eq!(encoded & 0b0011_1111, original_byte & 0b0011_1111);
1395 }
1396
1397 #[test]
1398 fn test_fault_code_comm_error_bit_order() {
1399 let byte = 0x01;
1403 let fault = FaultCodeCommError::from(u8::new(byte));
1404 assert!(fault.joint1_comm_error(), "1号关节应该通信异常");
1405 assert!(!fault.joint2_comm_error(), "2号关节应该正常");
1406
1407 let byte = 0x02;
1411 let fault = FaultCodeCommError::from(u8::new(byte));
1412 assert!(!fault.joint1_comm_error(), "1号关节应该正常");
1413 assert!(fault.joint2_comm_error(), "2号关节应该通信异常");
1414 }
1415
1416 #[test]
1417 fn test_fault_code_comm_error_encode() {
1418 let mut fault = FaultCodeCommError::from(u8::new(0));
1422 fault.set_joint2_comm_error(true);
1423 fault.set_joint6_comm_error(true);
1424
1425 let encoded = u8::from(fault).value();
1426 assert_eq!(encoded, 0x22, "编码值应该是 0x22 (Bit 1, 5 = 1)");
1427 }
1428
1429 #[test]
1430 fn test_fault_code_comm_error_all_joints() {
1431 let mut fault = FaultCodeCommError::from(u8::new(0));
1435 fault.set_joint1_comm_error(true);
1436 fault.set_joint2_comm_error(true);
1437 fault.set_joint3_comm_error(true);
1438 fault.set_joint4_comm_error(true);
1439 fault.set_joint5_comm_error(true);
1440 fault.set_joint6_comm_error(true);
1441
1442 let encoded = u8::from(fault).value();
1443 assert_eq!(encoded & 0b0011_1111, 0x3F, "前6位应该都是1");
1444 }
1445
1446 #[test]
1451 fn test_robot_status_feedback_parse() {
1452 let frame = PiperFrame::new_standard(
1453 ID_ROBOT_STATUS as u16,
1454 &[
1455 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0b0011_1111, 0b0000_0000, ],
1464 );
1465
1466 let status = RobotStatusFeedback::try_from(frame).unwrap();
1467
1468 assert_eq!(status.control_mode, ControlMode::CanControl);
1469 assert_eq!(status.robot_status, RobotStatus::Normal);
1470 assert_eq!(status.move_mode, MoveMode::MoveJ);
1471 assert_eq!(status.teach_status, TeachStatus::Closed);
1472 assert_eq!(status.motion_status, MotionStatus::Arrived);
1473 assert_eq!(status.trajectory_point_index, 5);
1474 assert!(status.fault_code_angle_limit.joint1_limit());
1475 assert!(status.fault_code_angle_limit.joint6_limit());
1476 assert!(!status.fault_code_comm_error.joint1_comm_error());
1477 }
1478
1479 #[test]
1480 fn test_robot_status_feedback_invalid_id() {
1481 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
1482 let result = RobotStatusFeedback::try_from(frame);
1483 assert!(result.is_err());
1484 match result.unwrap_err() {
1485 ProtocolError::InvalidCanId { id } => assert_eq!(id, 0x999),
1486 _ => panic!("Expected InvalidCanId error"),
1487 }
1488 }
1489
1490 #[test]
1491 fn test_robot_status_feedback_invalid_length() {
1492 let frame = PiperFrame::new_standard(ID_ROBOT_STATUS as u16, &[0; 4]);
1493 let result = RobotStatusFeedback::try_from(frame);
1494 assert!(result.is_err());
1495 match result.unwrap_err() {
1496 ProtocolError::InvalidLength { expected, actual } => {
1497 assert_eq!(expected, 8);
1498 assert_eq!(actual, 4);
1499 },
1500 _ => panic!("Expected InvalidLength error"),
1501 }
1502 }
1503
1504 #[test]
1505 fn test_robot_status_feedback_all_fields() {
1506 let frame = PiperFrame::new_standard(
1508 ID_ROBOT_STATUS as u16,
1509 &[
1510 0x07, 0x0F, 0x04, 0x07, 0x01, 0xFF, 0b0011_1111, 0b0011_1111, ],
1519 );
1520
1521 let status = RobotStatusFeedback::try_from(frame).unwrap();
1522
1523 assert_eq!(status.control_mode, ControlMode::OfflineTrajectory);
1524 assert_eq!(status.robot_status, RobotStatus::ResistorOverTemp);
1525 assert_eq!(status.move_mode, MoveMode::MoveM);
1526 assert_eq!(status.teach_status, TeachStatus::MoveToStart);
1527 assert_eq!(status.motion_status, MotionStatus::NotArrived);
1528 assert_eq!(status.trajectory_point_index, 0xFF);
1529
1530 assert!(status.fault_code_angle_limit.joint1_limit());
1532 assert!(status.fault_code_angle_limit.joint2_limit());
1533 assert!(status.fault_code_angle_limit.joint3_limit());
1534 assert!(status.fault_code_angle_limit.joint4_limit());
1535 assert!(status.fault_code_angle_limit.joint5_limit());
1536 assert!(status.fault_code_angle_limit.joint6_limit());
1537
1538 assert!(status.fault_code_comm_error.joint1_comm_error());
1540 assert!(status.fault_code_comm_error.joint2_comm_error());
1541 assert!(status.fault_code_comm_error.joint3_comm_error());
1542 assert!(status.fault_code_comm_error.joint4_comm_error());
1543 assert!(status.fault_code_comm_error.joint5_comm_error());
1544 assert!(status.fault_code_comm_error.joint6_comm_error());
1545 }
1546
1547 #[test]
1552 fn test_joint_feedback12_parse() {
1553 let j1_val = 90000i32;
1555 let j2_val = -45000i32;
1556 let mut data = [0u8; 8];
1557 data[0..4].copy_from_slice(&j1_val.to_be_bytes());
1558 data[4..8].copy_from_slice(&j2_val.to_be_bytes());
1559 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &data);
1560
1561 let feedback = JointFeedback12::try_from(frame).unwrap();
1562 assert_eq!(feedback.j1_raw(), 90000);
1563 assert_eq!(feedback.j2_raw(), -45000);
1564 assert!((feedback.j1() - 90.0).abs() < 0.0001);
1565 assert!((feedback.j2() - (-45.0)).abs() < 0.0001);
1566 }
1567
1568 #[test]
1569 fn test_joint_feedback12_physical_conversion() {
1570 let frame = PiperFrame::new_standard(
1572 ID_JOINT_FEEDBACK_12 as u16,
1573 &[
1574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, ],
1577 );
1578
1579 let feedback = JointFeedback12::try_from(frame).unwrap();
1580 assert_eq!(feedback.j1(), 0.0);
1581 assert!((feedback.j2() - 0.5).abs() < 0.0001);
1582
1583 assert!((feedback.j1_rad() - 0.0).abs() < 0.0001);
1585 assert!((feedback.j2_rad() - (0.5 * std::f64::consts::PI / 180.0)).abs() < 0.0001);
1586 }
1587
1588 #[test]
1589 fn test_joint_feedback12_boundary_values() {
1590 let max_positive = i32::MAX;
1592 let mut data = [0u8; 8];
1593 data[0..4].copy_from_slice(&max_positive.to_be_bytes());
1594 data[4..8].copy_from_slice(&max_positive.to_be_bytes());
1595 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &data);
1596
1597 let feedback = JointFeedback12::try_from(frame).unwrap();
1598 assert_eq!(feedback.j1_raw(), max_positive);
1599 assert_eq!(feedback.j2_raw(), max_positive);
1600
1601 let min_negative = i32::MIN;
1603 let mut data = [0u8; 8];
1604 data[0..4].copy_from_slice(&min_negative.to_be_bytes());
1605 data[4..8].copy_from_slice(&min_negative.to_be_bytes());
1606 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &data);
1607
1608 let feedback = JointFeedback12::try_from(frame).unwrap();
1609 assert_eq!(feedback.j1_raw(), min_negative);
1610 assert_eq!(feedback.j2_raw(), min_negative);
1611 }
1612
1613 #[test]
1614 fn test_joint_feedback12_invalid_id() {
1615 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
1616 let result = JointFeedback12::try_from(frame);
1617 assert!(result.is_err());
1618 match result.unwrap_err() {
1619 ProtocolError::InvalidCanId { id } => assert_eq!(id, 0x999),
1620 _ => panic!("Expected InvalidCanId error"),
1621 }
1622 }
1623
1624 #[test]
1625 fn test_joint_feedback12_invalid_length() {
1626 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &[0; 4]);
1627 let result = JointFeedback12::try_from(frame);
1628 assert!(result.is_err());
1629 match result.unwrap_err() {
1630 ProtocolError::InvalidLength { expected, actual } => {
1631 assert_eq!(expected, 8);
1632 assert_eq!(actual, 4);
1633 },
1634 _ => panic!("Expected InvalidLength error"),
1635 }
1636 }
1637
1638 #[test]
1639 fn test_joint_feedback34_parse() {
1640 let j3_val = 30000i32;
1642 let j4_val = -60000i32;
1643 let mut data = [0u8; 8];
1644 data[0..4].copy_from_slice(&j3_val.to_be_bytes());
1645 data[4..8].copy_from_slice(&j4_val.to_be_bytes());
1646 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_34 as u16, &data);
1647
1648 let feedback = JointFeedback34::try_from(frame).unwrap();
1649 assert_eq!(feedback.j3_raw(), 30000);
1650 assert_eq!(feedback.j4_raw(), -60000);
1651 assert!((feedback.j3() - 30.0).abs() < 0.0001);
1652 assert!((feedback.j4() - (-60.0)).abs() < 0.0001);
1653 }
1654
1655 #[test]
1656 fn test_joint_feedback56_parse() {
1657 let j5_val = 180000i32;
1659 let j6_val = -90000i32;
1660 let mut data = [0u8; 8];
1661 data[0..4].copy_from_slice(&j5_val.to_be_bytes());
1662 data[4..8].copy_from_slice(&j6_val.to_be_bytes());
1663 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_56 as u16, &data);
1664
1665 let feedback = JointFeedback56::try_from(frame).unwrap();
1666 assert_eq!(feedback.j5_raw(), 180000);
1667 assert_eq!(feedback.j6_raw(), -90000);
1668 assert!((feedback.j5() - 180.0).abs() < 0.0001);
1669 assert!((feedback.j6() - (-90.0)).abs() < 0.0001);
1670 }
1671
1672 #[test]
1673 fn test_joint_feedback_roundtrip() {
1674 let test_cases = vec![
1676 (0i32, 0i32),
1677 (90000i32, -45000i32),
1678 (i32::MAX, i32::MIN),
1679 (180000i32, -90000i32),
1680 ];
1681
1682 for (j1_val, j2_val) in test_cases {
1683 let frame = PiperFrame::new_standard(
1684 ID_JOINT_FEEDBACK_12 as u16,
1685 &[
1686 j1_val.to_be_bytes()[0],
1687 j1_val.to_be_bytes()[1],
1688 j1_val.to_be_bytes()[2],
1689 j1_val.to_be_bytes()[3],
1690 j2_val.to_be_bytes()[0],
1691 j2_val.to_be_bytes()[1],
1692 j2_val.to_be_bytes()[2],
1693 j2_val.to_be_bytes()[3],
1694 ],
1695 );
1696
1697 let feedback = JointFeedback12::try_from(frame).unwrap();
1698 assert_eq!(feedback.j1_raw(), j1_val);
1699 assert_eq!(feedback.j2_raw(), j2_val);
1700 }
1701 }
1702
1703 #[test]
1708 fn test_end_pose_feedback1_parse() {
1709 let x_val = 100000i32;
1711 let y_val = -50000i32;
1712 let mut data = [0u8; 8];
1713 data[0..4].copy_from_slice(&x_val.to_be_bytes());
1714 data[4..8].copy_from_slice(&y_val.to_be_bytes());
1715 let frame = PiperFrame::new_standard(ID_END_POSE_1 as u16, &data);
1716
1717 let feedback = EndPoseFeedback1::try_from(frame).unwrap();
1718 assert_eq!(feedback.x_raw(), 100000);
1719 assert_eq!(feedback.y_raw(), -50000);
1720 assert!((feedback.x() - 100.0).abs() < 0.0001);
1721 assert!((feedback.y() - (-50.0)).abs() < 0.0001);
1722 }
1723
1724 #[test]
1725 fn test_end_pose_feedback1_unit_conversion() {
1726 let x_val = 1234i32; let y_val = -5678i32; let mut data = [0u8; 8];
1730 data[0..4].copy_from_slice(&x_val.to_be_bytes());
1731 data[4..8].copy_from_slice(&y_val.to_be_bytes());
1732 let frame = PiperFrame::new_standard(ID_END_POSE_1 as u16, &data);
1733
1734 let feedback = EndPoseFeedback1::try_from(frame).unwrap();
1735 assert!((feedback.x() - 1.234).abs() < 0.0001);
1736 assert!((feedback.y() - (-5.678)).abs() < 0.0001);
1737 }
1738
1739 #[test]
1740 fn test_end_pose_feedback1_invalid_id() {
1741 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
1742 let result = EndPoseFeedback1::try_from(frame);
1743 assert!(result.is_err());
1744 }
1745
1746 #[test]
1747 fn test_end_pose_feedback2_parse() {
1748 let z_val = 200000i32;
1750 let rx_val = 45000i32;
1751 let mut data = [0u8; 8];
1752 data[0..4].copy_from_slice(&z_val.to_be_bytes());
1753 data[4..8].copy_from_slice(&rx_val.to_be_bytes());
1754 let frame = PiperFrame::new_standard(ID_END_POSE_2 as u16, &data);
1755
1756 let feedback = EndPoseFeedback2::try_from(frame).unwrap();
1757 assert_eq!(feedback.z_raw(), 200000);
1758 assert_eq!(feedback.rx_raw(), 45000);
1759 assert!((feedback.z() - 200.0).abs() < 0.0001);
1760 assert!((feedback.rx() - 45.0).abs() < 0.0001);
1761 assert!((feedback.rx_rad() - (45.0 * std::f64::consts::PI / 180.0)).abs() < 0.0001);
1762 }
1763
1764 #[test]
1765 fn test_end_pose_feedback3_parse() {
1766 let ry_val = 90000i32;
1768 let rz_val = -30000i32;
1769 let mut data = [0u8; 8];
1770 data[0..4].copy_from_slice(&ry_val.to_be_bytes());
1771 data[4..8].copy_from_slice(&rz_val.to_be_bytes());
1772 let frame = PiperFrame::new_standard(ID_END_POSE_3 as u16, &data);
1773
1774 let feedback = EndPoseFeedback3::try_from(frame).unwrap();
1775 assert_eq!(feedback.ry_raw(), 90000);
1776 assert_eq!(feedback.rz_raw(), -30000);
1777 assert!((feedback.ry() - 90.0).abs() < 0.0001);
1778 assert!((feedback.rz() - (-30.0)).abs() < 0.0001);
1779 }
1780
1781 #[test]
1786 fn test_joint_driver_high_speed_feedback_parse() {
1787 let speed_val = 1500i16;
1793 let current_val = 2500u16;
1794 let position_val = 1000000i32; let mut data = [0u8; 8];
1797 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1798 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1799 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1800
1801 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1802 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1803
1804 assert_eq!(feedback.joint_index, 1);
1805 assert_eq!(feedback.speed_raw(), 1500);
1806 assert_eq!(feedback.current_raw(), 2500i16); assert_eq!(feedback.position_raw(), 1000000);
1808 assert!((feedback.speed() - 1.5).abs() < 0.0001);
1809 assert!((feedback.current() - 2.5).abs() < 0.0001);
1810 }
1813
1814 #[test]
1815 fn test_joint_driver_high_speed_feedback_all_joints() {
1816 for joint_id in 1..=6 {
1818 let can_id = ID_JOINT_DRIVER_HIGH_SPEED_BASE + (joint_id - 1);
1819 let frame = PiperFrame::new_standard(can_id as u16, &[0; 8]);
1820 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1821 assert_eq!(feedback.joint_index, joint_id as u8);
1822 }
1823 }
1824
1825 #[test]
1826 fn test_joint_driver_high_speed_feedback_physical_conversion() {
1827 let speed_val = 3141i16; let current_val = 5000u16; let position_val = 3141592i32; let mut data = [0u8; 8];
1833 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1834 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1835 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1836
1837 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1838 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1839
1840 assert!((feedback.speed() - std::f64::consts::PI).abs() < 0.001);
1841 assert!((feedback.current() - 5.0).abs() < 0.001);
1842 assert_eq!(feedback.position_raw(), position_val);
1845 }
1846
1847 #[test]
1848 fn test_joint_driver_high_speed_feedback_boundary_values() {
1849 let speed_val = i16::MAX;
1852 let current_val = i16::MAX; let position_val = i32::MAX;
1854
1855 let mut data = [0u8; 8];
1856 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1857 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1858 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1859
1860 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1861 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1862
1863 assert_eq!(feedback.speed_raw(), i16::MAX);
1864 assert_eq!(feedback.current_raw(), i16::MAX);
1865 assert_eq!(feedback.position_raw(), i32::MAX);
1866 }
1867
1868 #[test]
1869 fn test_joint_driver_high_speed_feedback_invalid_id() {
1870 let frame = PiperFrame::new_standard(0x250, &[0; 8]); let result = JointDriverHighSpeedFeedback::try_from(frame);
1873 assert!(result.is_err());
1874
1875 let frame = PiperFrame::new_standard(0x257, &[0; 8]); let result = JointDriverHighSpeedFeedback::try_from(frame);
1877 assert!(result.is_err());
1878 }
1879
1880 #[test]
1881 fn test_joint_driver_high_speed_feedback_invalid_length() {
1882 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &[0; 4]);
1883 let result = JointDriverHighSpeedFeedback::try_from(frame);
1884 assert!(result.is_err());
1885 match result.unwrap_err() {
1886 ProtocolError::InvalidLength { expected, actual } => {
1887 assert_eq!(expected, 8);
1888 assert_eq!(actual, 4);
1889 },
1890 _ => panic!("Expected InvalidLength error"),
1891 }
1892 }
1893
1894 #[test]
1895 fn test_joint_driver_high_speed_feedback_negative_speed() {
1896 let speed_val = -1000i16; let current_val = 1000u16; let position_val = 0i32;
1900
1901 let mut data = [0u8; 8];
1902 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1903 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1904 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1905
1906 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1907 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1908
1909 assert_eq!(feedback.speed_raw(), -1000);
1910 assert!((feedback.speed() - (-1.0)).abs() < 0.0001);
1911 }
1912
1913 #[test]
1914 fn test_joint_driver_high_speed_feedback_torque_joints_1_3() {
1915 let current_val = 1000u16; let mut data = [0u8; 8];
1919 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1920
1921 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1922 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1923
1924 assert_eq!(feedback.joint_index, 1);
1925 let expected_torque = 1.0 * JointDriverHighSpeedFeedback::COEFFICIENT_1_3; assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1927 assert_eq!(
1928 feedback.torque_raw(),
1929 (expected_torque * 1000.0).round() as i32
1930 );
1931
1932 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 1, &data);
1934 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1935 assert_eq!(feedback.joint_index, 2);
1936 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1937
1938 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 2, &data);
1940 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1941 assert_eq!(feedback.joint_index, 3);
1942 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1943 }
1944
1945 #[test]
1946 fn test_joint_driver_high_speed_feedback_torque_joints_4_6() {
1947 let current_val = 1000u16; let mut data = [0u8; 8];
1951 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1952
1953 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 3, &data);
1954 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1955
1956 assert_eq!(feedback.joint_index, 4);
1957 let expected_torque = 1.0 * JointDriverHighSpeedFeedback::COEFFICIENT_4_6; assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1959 assert_eq!(
1960 feedback.torque_raw(),
1961 (expected_torque * 1000.0).round() as i32
1962 );
1963
1964 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 4, &data);
1966 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1967 assert_eq!(feedback.joint_index, 5);
1968 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1969
1970 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 5, &data);
1972 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1973 assert_eq!(feedback.joint_index, 6);
1974 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1975 }
1976
1977 #[test]
1978 fn test_joint_driver_high_speed_feedback_torque_with_custom_current() {
1979 let current_val = 2000u16; let custom_current = 2.5; let mut data = [0u8; 8];
1984 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1985
1986 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1988 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1989 let torque_from_feedback = feedback.torque(None);
1990 let expected_from_feedback = 2.0 * JointDriverHighSpeedFeedback::COEFFICIENT_1_3;
1991 assert!((torque_from_feedback - expected_from_feedback).abs() < 0.0001);
1992
1993 let torque_from_custom = feedback.torque(Some(custom_current));
1995 let expected_from_custom = custom_current * JointDriverHighSpeedFeedback::COEFFICIENT_1_3;
1996 assert!((torque_from_custom - expected_from_custom).abs() < 0.0001);
1997
1998 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 3, &data);
2000 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
2001 let torque_from_custom = feedback.torque(Some(custom_current));
2002 let expected_from_custom = custom_current * JointDriverHighSpeedFeedback::COEFFICIENT_4_6;
2003 assert!((torque_from_custom - expected_from_custom).abs() < 0.0001);
2004 }
2005
2006 #[test]
2007 fn test_joint_driver_high_speed_feedback_torque_coefficients() {
2008 assert_eq!(JointDriverHighSpeedFeedback::COEFFICIENT_1_3, 1.18125);
2010 assert_eq!(JointDriverHighSpeedFeedback::COEFFICIENT_4_6, 0.95844);
2011
2012 let current_val = 1000u16; let mut data = [0u8; 8];
2017 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
2018
2019 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
2020 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
2021 let torque = feedback.torque(None);
2022 assert!((torque - 1.18125).abs() < 0.0001);
2023 assert_eq!(feedback.torque_raw(), 1181); }
2025
2026 #[test]
2031 fn test_gripper_status_bit_order() {
2032 let byte = 0x41;
2037 let status = GripperStatus::from(u8::new(byte));
2038
2039 assert!(status.voltage_low(), "电压应该过低");
2040 assert!(!status.motor_over_temp(), "电机应该正常");
2041 assert!(status.enabled(), "应该使能(Bit 6 = 1)");
2042 assert!(!status.homed(), "应该没有回零");
2043 }
2044
2045 #[test]
2046 fn test_gripper_status_encode() {
2047 let mut status = GripperStatus::from(u8::new(0));
2048 status.set_voltage_low(true);
2049 status.set_enabled(true);
2050 status.set_homed(true);
2051
2052 let encoded = u8::from(status).value();
2053 assert_eq!(encoded, 0xC1);
2055 }
2056
2057 #[test]
2058 fn test_gripper_feedback_parse() {
2059 let travel_val = 50000i32;
2064 let torque_val = 2500i16;
2065 let status_byte = 0x41u8;
2066
2067 let mut data = [0u8; 8];
2068 data[0..4].copy_from_slice(&travel_val.to_be_bytes());
2069 data[4..6].copy_from_slice(&torque_val.to_be_bytes());
2070 data[6] = status_byte;
2071
2072 let frame = PiperFrame::new_standard(ID_GRIPPER_FEEDBACK as u16, &data);
2073 let feedback = GripperFeedback::try_from(frame).unwrap();
2074
2075 assert_eq!(feedback.travel_raw(), 50000);
2076 assert_eq!(feedback.torque_raw(), 2500);
2077 assert!((feedback.travel() - 50.0).abs() < 0.0001);
2078 assert!((feedback.torque() - 2.5).abs() < 0.0001);
2079 assert!(feedback.status.voltage_low());
2080 assert!(feedback.status.enabled());
2081 }
2082
2083 #[test]
2084 fn test_gripper_feedback_invalid_id() {
2085 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2086 let result = GripperFeedback::try_from(frame);
2087 assert!(result.is_err());
2088 }
2089
2090 #[test]
2091 fn test_gripper_feedback_invalid_length() {
2092 let frame = PiperFrame::new_standard(ID_GRIPPER_FEEDBACK as u16, &[0; 4]);
2093 let result = GripperFeedback::try_from(frame);
2094 assert!(result.is_err());
2095 }
2096
2097 #[test]
2098 fn test_gripper_status_all_flags() {
2099 let mut status = GripperStatus::from(u8::new(0));
2101 status.set_voltage_low(true);
2102 status.set_motor_over_temp(true);
2103 status.set_driver_over_current(true);
2104 status.set_driver_over_temp(true);
2105 status.set_sensor_error(true);
2106 status.set_driver_error(true);
2107 status.set_enabled(true);
2108 status.set_homed(true);
2109
2110 let encoded = u8::from(status).value();
2111 assert_eq!(encoded, 0xFF); }
2113
2114 #[test]
2119 fn test_driver_status_bit_order() {
2120 let byte = 0x55;
2127 let status = DriverStatus::from(u8::new(byte));
2128
2129 assert!(status.voltage_low(), "电源电压应该过低");
2130 assert!(!status.motor_over_temp(), "电机应该正常温度");
2131 assert!(status.driver_over_current(), "驱动器应该过流");
2132 assert!(status.collision_protection(), "碰撞保护应该触发");
2133 assert!(status.enabled(), "驱动器应该使能");
2134 }
2135
2136 #[test]
2137 fn test_driver_status_encode() {
2138 let mut status = DriverStatus::from(u8::new(0));
2139 status.set_voltage_low(true);
2140 status.set_driver_over_current(true);
2141 status.set_collision_protection(true);
2142 status.set_enabled(true);
2143
2144 let encoded = u8::from(status).value();
2145 assert_eq!(encoded, 0x55);
2147 }
2148
2149 #[test]
2150 fn test_joint_driver_low_speed_feedback_parse() {
2151 let voltage_val = 240u16;
2158 let driver_temp_val = 45i16;
2159 let motor_temp_val = 50i8;
2160 let status_byte = 0x44u8; let bus_current_val = 5000u16;
2162
2163 let mut data = [0u8; 8];
2164 data[0..2].copy_from_slice(&voltage_val.to_be_bytes());
2165 data[2..4].copy_from_slice(&driver_temp_val.to_be_bytes());
2166 data[4] = motor_temp_val as u8;
2167 data[5] = status_byte;
2168 data[6..8].copy_from_slice(&bus_current_val.to_be_bytes());
2169
2170 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_LOW_SPEED_BASE as u16, &data);
2171 let feedback = JointDriverLowSpeedFeedback::try_from(frame).unwrap();
2172
2173 assert_eq!(feedback.joint_index, 1);
2174 assert_eq!(feedback.voltage, 240);
2175 assert_eq!(feedback.driver_temp, 45);
2176 assert_eq!(feedback.motor_temp, 50);
2177 assert_eq!(feedback.bus_current, 5000);
2178 assert!(feedback.status.driver_over_current());
2179 assert!(feedback.status.enabled());
2180 }
2181
2182 #[test]
2183 fn test_joint_driver_low_speed_feedback_all_joints() {
2184 for i in 1..=6 {
2186 let id = ID_JOINT_DRIVER_LOW_SPEED_BASE + (i - 1) as u32;
2187 let mut data = [0u8; 8];
2188 data[0..2].copy_from_slice(&240u16.to_be_bytes()); data[2..4].copy_from_slice(&45i16.to_be_bytes()); data[4] = 50; data[5] = 0x40; let frame = PiperFrame::new_standard(id as u16, &data);
2194 let feedback = JointDriverLowSpeedFeedback::try_from(frame).unwrap();
2195 assert_eq!(feedback.joint_index, i);
2196 }
2197 }
2198
2199 #[test]
2200 fn test_joint_driver_low_speed_feedback_conversions() {
2201 let mut data = [0u8; 8];
2202 data[0..2].copy_from_slice(&240u16.to_be_bytes()); data[2..4].copy_from_slice(&45i16.to_be_bytes()); data[4] = 50; data[5] = 0x40; data[6..8].copy_from_slice(&5000u16.to_be_bytes()); let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_LOW_SPEED_BASE as u16, &data);
2209 let feedback = JointDriverLowSpeedFeedback::try_from(frame).unwrap();
2210
2211 assert!((feedback.voltage() - 24.0).abs() < 0.01);
2212 assert!((feedback.driver_temp() - 45.0).abs() < 0.01);
2213 assert!((feedback.motor_temp() - 50.0).abs() < 0.01);
2214 assert!((feedback.bus_current() - 5.0).abs() < 0.001);
2215 }
2216
2217 #[test]
2218 fn test_joint_driver_low_speed_feedback_invalid_id() {
2219 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2220 let result = JointDriverLowSpeedFeedback::try_from(frame);
2221 assert!(result.is_err());
2222 }
2223
2224 #[test]
2225 fn test_joint_driver_low_speed_feedback_invalid_length() {
2226 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_LOW_SPEED_BASE as u16, &[0; 4]);
2227 let result = JointDriverLowSpeedFeedback::try_from(frame);
2228 assert!(result.is_err());
2229 }
2230
2231 #[test]
2236 fn test_joint_end_velocity_accel_feedback_parse() {
2237 let linear_vel = 1000u16; let angular_vel = 5000u16; let linear_accel = 2000u16; let angular_accel = 3000u16; let mut data = [0u8; 8];
2244 data[0..2].copy_from_slice(&linear_vel.to_be_bytes());
2245 data[2..4].copy_from_slice(&angular_vel.to_be_bytes());
2246 data[4..6].copy_from_slice(&linear_accel.to_be_bytes());
2247 data[6..8].copy_from_slice(&angular_accel.to_be_bytes());
2248
2249 let frame = PiperFrame::new_standard(ID_JOINT_END_VELOCITY_ACCEL_BASE as u16, &data);
2250 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2251
2252 assert_eq!(feedback.joint_index, 1);
2253 assert_eq!(feedback.linear_velocity_m_s_raw, 1000);
2254 assert_eq!(feedback.angular_velocity_rad_s_raw, 5000);
2255 assert_eq!(feedback.linear_accel_m_s2_raw, 2000);
2256 assert_eq!(feedback.angular_accel_rad_s2_raw, 3000);
2257 }
2258
2259 #[test]
2260 fn test_joint_end_velocity_accel_feedback_all_joints() {
2261 for i in 1..=6 {
2263 let id = ID_JOINT_END_VELOCITY_ACCEL_BASE + (i - 1) as u32;
2264 let mut data = [0u8; 8];
2265 data[0..2].copy_from_slice(&1000u16.to_be_bytes());
2266 data[2..4].copy_from_slice(&2000u16.to_be_bytes());
2267 data[4..6].copy_from_slice(&3000u16.to_be_bytes());
2268 data[6..8].copy_from_slice(&4000u16.to_be_bytes());
2269
2270 let frame = PiperFrame::new_standard(id as u16, &data);
2271 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2272 assert_eq!(feedback.joint_index, i);
2273 }
2274 }
2275
2276 #[test]
2277 fn test_joint_end_velocity_accel_feedback_conversions() {
2278 let mut data = [0u8; 8];
2279 data[0..2].copy_from_slice(&1000u16.to_be_bytes()); data[2..4].copy_from_slice(&2000u16.to_be_bytes()); data[4..6].copy_from_slice(&3000u16.to_be_bytes()); data[6..8].copy_from_slice(&4000u16.to_be_bytes()); let frame = PiperFrame::new_standard(ID_JOINT_END_VELOCITY_ACCEL_BASE as u16, &data);
2285 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2286
2287 assert!((feedback.linear_velocity() - 1.0).abs() < 0.0001);
2288 assert!((feedback.angular_velocity() - 2.0).abs() < 0.0001);
2289 assert!((feedback.linear_accel() - 3.0).abs() < 0.0001);
2290 assert!((feedback.angular_accel() - 4.0).abs() < 0.0001);
2291 }
2292
2293 #[test]
2294 fn test_joint_end_velocity_accel_feedback_zero() {
2295 let data = [0u8; 8];
2297 let frame = PiperFrame::new_standard(ID_JOINT_END_VELOCITY_ACCEL_BASE as u16, &data);
2298 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2299
2300 assert_eq!(feedback.linear_velocity_m_s_raw, 0);
2301 assert_eq!(feedback.angular_velocity_rad_s_raw, 0);
2302 assert_eq!(feedback.linear_accel_m_s2_raw, 0);
2303 assert_eq!(feedback.angular_accel_rad_s2_raw, 0);
2304 assert!((feedback.linear_velocity() - 0.0).abs() < 0.0001);
2305 assert!((feedback.angular_velocity() - 0.0).abs() < 0.0001);
2306 }
2307
2308 #[test]
2309 fn test_joint_end_velocity_accel_feedback_invalid_id() {
2310 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2311 let result = JointEndVelocityAccelFeedback::try_from(frame);
2312 assert!(result.is_err());
2313 }
2314
2315 #[test]
2316 fn test_joint_end_velocity_accel_feedback_invalid_length() {
2317 let frame = PiperFrame::new_standard(ID_JOINT_END_VELOCITY_ACCEL_BASE as u16, &[0; 7]);
2319 let result = JointEndVelocityAccelFeedback::try_from(frame);
2320 assert!(result.is_err());
2321 }
2322}
2323
2324#[derive(Debug, Clone, Default)]
2333pub struct FirmwareReadFeedback {
2334 pub firmware_data: [u8; 8],
2335}
2336
2337impl FirmwareReadFeedback {
2338 pub fn firmware_data(&self) -> &[u8; 8] {
2340 &self.firmware_data
2341 }
2342
2343 pub fn parse_version_string(accumulated_data: &[u8]) -> Option<String> {
2356 if let Some(version_start) = accumulated_data.windows(3).position(|w| w == b"S-V") {
2358 let version_length = 8;
2360 let version_end = (version_start + version_length).min(accumulated_data.len());
2362
2363 let version_bytes = &accumulated_data[version_start..version_end];
2365
2366 Some(String::from_utf8_lossy(version_bytes).trim().to_string())
2369 } else {
2370 None
2371 }
2372 }
2373}
2374
2375impl TryFrom<PiperFrame> for FirmwareReadFeedback {
2376 type Error = ProtocolError;
2377
2378 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2379 if frame.id != ID_FIRMWARE_READ {
2381 return Err(ProtocolError::InvalidCanId { id: frame.id });
2382 }
2383
2384 if frame.len == 0 {
2386 return Err(ProtocolError::InvalidLength {
2387 expected: 1,
2388 actual: 0,
2389 });
2390 }
2391
2392 let mut firmware_data = [0u8; 8];
2393 let copy_len = (frame.len as usize).min(8);
2394 firmware_data[..copy_len].copy_from_slice(&frame.data[..copy_len]);
2395
2396 Ok(Self { firmware_data })
2397 }
2398}
2399
2400#[cfg(test)]
2401mod firmware_read_tests {
2402 use super::*;
2403
2404 #[test]
2405 fn test_firmware_read_feedback_parse() {
2406 let data = b"S-V1.6-3";
2408 let frame = PiperFrame::new_standard(ID_FIRMWARE_READ as u16, data);
2409 let feedback = FirmwareReadFeedback::try_from(frame).unwrap();
2410
2411 assert_eq!(&feedback.firmware_data[..8], data);
2412 }
2413
2414 #[test]
2415 fn test_firmware_read_feedback_parse_version_string() {
2416 let accumulated_data = b"Some prefix S-V1.6-3\nOther data";
2418 let version = FirmwareReadFeedback::parse_version_string(accumulated_data);
2419 assert_eq!(version, Some("S-V1.6-3".to_string()));
2421 }
2422
2423 #[test]
2424 fn test_firmware_read_feedback_parse_version_string_fixed_length() {
2425 let accumulated_data = b"Some prefix S-V1.6-3\nOther data";
2427 let version = FirmwareReadFeedback::parse_version_string(accumulated_data);
2428 assert_eq!(version, Some("S-V1.6-3".to_string()));
2430 }
2431
2432 #[test]
2433 fn test_firmware_read_feedback_parse_version_string_short() {
2434 let accumulated_data = b"S-V1.6";
2436 let version = FirmwareReadFeedback::parse_version_string(accumulated_data);
2437 assert_eq!(version, Some("S-V1.6".to_string()));
2439 }
2440
2441 #[test]
2442 fn test_firmware_read_feedback_parse_version_string_invalid_utf8() {
2443 let data = vec![b'S', b'-', b'V', 0xFF, 0xFE, b'1', b'.', b'6'];
2445 let version = FirmwareReadFeedback::parse_version_string(&data);
2446 assert!(version.is_some());
2448 let version_str = version.unwrap();
2449 assert!(version_str.contains("S-V"));
2451 }
2452
2453 #[test]
2454 fn test_firmware_read_feedback_parse_version_string_not_found() {
2455 let accumulated_data = b"Some data without version";
2457 let version = FirmwareReadFeedback::parse_version_string(accumulated_data);
2458 assert_eq!(version, None);
2459 }
2460
2461 #[test]
2462 fn test_firmware_read_feedback_invalid_id() {
2463 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2464 let result = FirmwareReadFeedback::try_from(frame);
2465 assert!(result.is_err());
2466 }
2467
2468 #[test]
2469 fn test_firmware_read_feedback_empty_data() {
2470 let frame = PiperFrame::new_standard(ID_FIRMWARE_READ as u16, &[]);
2471 let result = FirmwareReadFeedback::try_from(frame);
2472 assert!(result.is_err());
2473 }
2474}
2475
2476#[derive(Debug, Clone, Copy, Default)]
2487pub struct ControlModeCommandFeedback {
2488 pub control_mode: ControlModeCommand,
2489 pub move_mode: MoveMode,
2490 pub speed_percent: u8,
2491 pub mit_mode: MitMode,
2492 pub trajectory_stay_time: u8,
2493 pub install_position: InstallPosition,
2494}
2495
2496impl TryFrom<PiperFrame> for ControlModeCommandFeedback {
2497 type Error = ProtocolError;
2498
2499 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2500 if frame.id != ID_CONTROL_MODE {
2502 return Err(ProtocolError::InvalidCanId { id: frame.id });
2503 }
2504
2505 if frame.len < 6 {
2507 return Err(ProtocolError::InvalidLength {
2508 expected: 6,
2509 actual: frame.len as usize,
2510 });
2511 }
2512
2513 Ok(Self {
2514 control_mode: ControlModeCommand::try_from(frame.data[0])?,
2515 move_mode: MoveMode::from(frame.data[1]),
2516 speed_percent: frame.data[2],
2517 mit_mode: MitMode::try_from(frame.data[3])?,
2518 trajectory_stay_time: frame.data[4],
2519 install_position: InstallPosition::try_from(frame.data[5])?,
2520 })
2521 }
2522}
2523
2524#[derive(Debug, Clone, Copy, Default)]
2529pub struct JointControlFeedback {
2530 pub j1_deg: i32,
2531 pub j2_deg: i32,
2532 pub j3_deg: i32,
2533 pub j4_deg: i32,
2534 pub j5_deg: i32,
2535 pub j6_deg: i32,
2536}
2537
2538impl JointControlFeedback {
2539 pub fn update_from_12(&mut self, feedback: JointControl12Feedback) {
2541 self.j1_deg = feedback.j1_deg;
2542 self.j2_deg = feedback.j2_deg;
2543 }
2544
2545 pub fn update_from_34(&mut self, feedback: JointControl34Feedback) {
2547 self.j3_deg = feedback.j3_deg;
2548 self.j4_deg = feedback.j4_deg;
2549 }
2550
2551 pub fn update_from_56(&mut self, feedback: JointControl56Feedback) {
2553 self.j5_deg = feedback.j5_deg;
2554 self.j6_deg = feedback.j6_deg;
2555 }
2556}
2557
2558#[derive(Debug, Clone, Copy, Default)]
2560pub struct JointControl12Feedback {
2561 pub j1_deg: i32,
2562 pub j2_deg: i32,
2563}
2564
2565impl TryFrom<PiperFrame> for JointControl12Feedback {
2566 type Error = ProtocolError;
2567
2568 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2569 if frame.id != ID_JOINT_CONTROL_12 {
2570 return Err(ProtocolError::InvalidCanId { id: frame.id });
2571 }
2572
2573 if frame.len < 8 {
2574 return Err(ProtocolError::InvalidLength {
2575 expected: 8,
2576 actual: frame.len as usize,
2577 });
2578 }
2579
2580 let j1_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2581 let j1_deg = bytes_to_i32_be(j1_bytes);
2582
2583 let j2_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
2584 let j2_deg = bytes_to_i32_be(j2_bytes);
2585
2586 Ok(Self { j1_deg, j2_deg })
2587 }
2588}
2589
2590#[derive(Debug, Clone, Copy, Default)]
2592pub struct JointControl34Feedback {
2593 pub j3_deg: i32,
2594 pub j4_deg: i32,
2595}
2596
2597impl TryFrom<PiperFrame> for JointControl34Feedback {
2598 type Error = ProtocolError;
2599
2600 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2601 if frame.id != ID_JOINT_CONTROL_34 {
2602 return Err(ProtocolError::InvalidCanId { id: frame.id });
2603 }
2604
2605 if frame.len < 8 {
2606 return Err(ProtocolError::InvalidLength {
2607 expected: 8,
2608 actual: frame.len as usize,
2609 });
2610 }
2611
2612 let j3_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2613 let j3_deg = bytes_to_i32_be(j3_bytes);
2614
2615 let j4_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
2616 let j4_deg = bytes_to_i32_be(j4_bytes);
2617
2618 Ok(Self { j3_deg, j4_deg })
2619 }
2620}
2621
2622#[derive(Debug, Clone, Copy, Default)]
2624pub struct JointControl56Feedback {
2625 pub j5_deg: i32,
2626 pub j6_deg: i32,
2627}
2628
2629impl TryFrom<PiperFrame> for JointControl56Feedback {
2630 type Error = ProtocolError;
2631
2632 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2633 if frame.id != ID_JOINT_CONTROL_56 {
2634 return Err(ProtocolError::InvalidCanId { id: frame.id });
2635 }
2636
2637 if frame.len < 8 {
2638 return Err(ProtocolError::InvalidLength {
2639 expected: 8,
2640 actual: frame.len as usize,
2641 });
2642 }
2643
2644 let j5_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2645 let j5_deg = bytes_to_i32_be(j5_bytes);
2646
2647 let j6_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
2648 let j6_deg = bytes_to_i32_be(j6_bytes);
2649
2650 Ok(Self { j5_deg, j6_deg })
2651 }
2652}
2653
2654#[derive(Debug, Clone, Copy, Default)]
2658pub struct GripperControlFeedback {
2659 pub travel_mm: i32,
2660 pub torque_nm: i16,
2661 pub status_code: u8,
2662 pub set_zero: u8,
2663}
2664
2665impl TryFrom<PiperFrame> for GripperControlFeedback {
2666 type Error = ProtocolError;
2667
2668 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2669 if frame.id != ID_GRIPPER_CONTROL {
2670 return Err(ProtocolError::InvalidCanId { id: frame.id });
2671 }
2672
2673 if frame.len < 8 {
2674 return Err(ProtocolError::InvalidLength {
2675 expected: 8,
2676 actual: frame.len as usize,
2677 });
2678 }
2679
2680 let travel_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2681 let travel_mm = bytes_to_i32_be(travel_bytes);
2682
2683 let torque_bytes = [frame.data[4], frame.data[5]];
2684 let torque_nm = bytes_to_i16_be(torque_bytes);
2685
2686 Ok(Self {
2687 travel_mm,
2688 torque_nm,
2689 status_code: frame.data[6],
2690 set_zero: frame.data[7],
2691 })
2692 }
2693}