1use crate::can::PiperFrame;
7use crate::protocol::control::{ControlModeCommand, InstallPosition, MitMode};
8use crate::protocol::{
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)]
29pub enum ControlMode {
30 #[default]
32 Standby = 0x00,
33 CanControl = 0x01,
35 Teach = 0x02,
37 Ethernet = 0x03,
39 Wifi = 0x04,
41 Remote = 0x05,
43 LinkTeach = 0x06,
45 OfflineTrajectory = 0x07,
47}
48
49impl From<u8> for ControlMode {
50 fn from(value: u8) -> Self {
51 match value {
52 0x00 => ControlMode::Standby,
53 0x01 => ControlMode::CanControl,
54 0x02 => ControlMode::Teach,
55 0x03 => ControlMode::Ethernet,
56 0x04 => ControlMode::Wifi,
57 0x05 => ControlMode::Remote,
58 0x06 => ControlMode::LinkTeach,
59 0x07 => ControlMode::OfflineTrajectory,
60 _ => ControlMode::Standby, }
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
67pub enum RobotStatus {
68 #[default]
70 Normal = 0x00,
71 EmergencyStop = 0x01,
73 NoSolution = 0x02,
75 Singularity = 0x03,
77 AngleLimitExceeded = 0x04,
79 JointCommError = 0x05,
81 JointBrakeNotOpen = 0x06,
83 Collision = 0x07,
85 TeachOverspeed = 0x08,
87 JointStatusError = 0x09,
89 OtherError = 0x0A,
91 TeachRecord = 0x0B,
93 TeachExecute = 0x0C,
95 TeachPause = 0x0D,
97 MainControlOverTemp = 0x0E,
99 ResistorOverTemp = 0x0F,
101}
102
103impl From<u8> for RobotStatus {
104 fn from(value: u8) -> Self {
105 match value {
106 0x00 => RobotStatus::Normal,
107 0x01 => RobotStatus::EmergencyStop,
108 0x02 => RobotStatus::NoSolution,
109 0x03 => RobotStatus::Singularity,
110 0x04 => RobotStatus::AngleLimitExceeded,
111 0x05 => RobotStatus::JointCommError,
112 0x06 => RobotStatus::JointBrakeNotOpen,
113 0x07 => RobotStatus::Collision,
114 0x08 => RobotStatus::TeachOverspeed,
115 0x09 => RobotStatus::JointStatusError,
116 0x0A => RobotStatus::OtherError,
117 0x0B => RobotStatus::TeachRecord,
118 0x0C => RobotStatus::TeachExecute,
119 0x0D => RobotStatus::TeachPause,
120 0x0E => RobotStatus::MainControlOverTemp,
121 0x0F => RobotStatus::ResistorOverTemp,
122 _ => RobotStatus::Normal, }
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
129pub enum MoveMode {
130 #[default]
132 MoveP = 0x00,
133 MoveJ = 0x01,
135 MoveL = 0x02,
137 MoveC = 0x03,
139 MoveM = 0x04,
141}
142
143impl From<u8> for MoveMode {
144 fn from(value: u8) -> Self {
145 match value {
146 0x00 => MoveMode::MoveP,
147 0x01 => MoveMode::MoveJ,
148 0x02 => MoveMode::MoveL,
149 0x03 => MoveMode::MoveC,
150 0x04 => MoveMode::MoveM,
151 _ => MoveMode::MoveP, }
153 }
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
158pub enum TeachStatus {
159 #[default]
161 Closed = 0x00,
162 StartRecord = 0x01,
164 EndRecord = 0x02,
166 Execute = 0x03,
168 Pause = 0x04,
170 Continue = 0x05,
172 Terminate = 0x06,
174 MoveToStart = 0x07,
176}
177
178impl From<u8> for TeachStatus {
179 fn from(value: u8) -> Self {
180 match value {
181 0x00 => TeachStatus::Closed,
182 0x01 => TeachStatus::StartRecord,
183 0x02 => TeachStatus::EndRecord,
184 0x03 => TeachStatus::Execute,
185 0x04 => TeachStatus::Pause,
186 0x05 => TeachStatus::Continue,
187 0x06 => TeachStatus::Terminate,
188 0x07 => TeachStatus::MoveToStart,
189 _ => TeachStatus::Closed, }
191 }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
196pub enum MotionStatus {
197 #[default]
199 Arrived = 0x00,
200 NotArrived = 0x01,
202}
203
204impl From<u8> for MotionStatus {
205 fn from(value: u8) -> Self {
206 match value {
207 0x00 => MotionStatus::Arrived,
208 0x01 => MotionStatus::NotArrived,
209 _ => MotionStatus::NotArrived, }
211 }
212}
213
214#[bitsize(8)]
233#[derive(FromBits, DebugBits, Clone, Copy, Default)]
234pub struct FaultCodeAngleLimit {
235 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, }
243
244#[bitsize(8)]
255#[derive(FromBits, DebugBits, Clone, Copy, Default)]
256pub struct FaultCodeCommError {
257 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, }
265
266#[derive(Debug, Clone, Copy, Default)]
275pub struct RobotStatusFeedback {
276 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, }
285
286impl TryFrom<PiperFrame> for RobotStatusFeedback {
287 type Error = ProtocolError;
288
289 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
290 if frame.id != ID_ROBOT_STATUS {
292 return Err(ProtocolError::InvalidCanId { id: frame.id });
293 }
294
295 if frame.len < 8 {
297 return Err(ProtocolError::InvalidLength {
298 expected: 8,
299 actual: frame.len as usize,
300 });
301 }
302
303 Ok(Self {
305 control_mode: ControlMode::from(frame.data[0]),
306 robot_status: RobotStatus::from(frame.data[1]),
307 move_mode: MoveMode::from(frame.data[2]),
308 teach_status: TeachStatus::from(frame.data[3]),
309 motion_status: MotionStatus::from(frame.data[4]),
310 trajectory_point_index: frame.data[5],
311 fault_code_angle_limit: FaultCodeAngleLimit::from(u8::new(frame.data[6])),
312 fault_code_comm_error: FaultCodeCommError::from(u8::new(frame.data[7])),
313 })
314 }
315}
316
317#[derive(Debug, Clone, Copy, Default)]
326pub struct JointFeedback12 {
327 pub j1_deg: i32, pub j2_deg: i32, }
330
331impl JointFeedback12 {
332 pub fn j1_raw(&self) -> i32 {
334 self.j1_deg
335 }
336
337 pub fn j2_raw(&self) -> i32 {
339 self.j2_deg
340 }
341
342 pub fn j1(&self) -> f64 {
344 self.j1_deg as f64 / 1000.0
345 }
346
347 pub fn j2(&self) -> f64 {
349 self.j2_deg as f64 / 1000.0
350 }
351
352 pub fn j1_rad(&self) -> f64 {
354 self.j1() * std::f64::consts::PI / 180.0
355 }
356
357 pub fn j2_rad(&self) -> f64 {
359 self.j2() * std::f64::consts::PI / 180.0
360 }
361}
362
363impl TryFrom<PiperFrame> for JointFeedback12 {
364 type Error = ProtocolError;
365
366 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
367 if frame.id != ID_JOINT_FEEDBACK_12 {
369 return Err(ProtocolError::InvalidCanId { id: frame.id });
370 }
371
372 if frame.len < 8 {
374 return Err(ProtocolError::InvalidLength {
375 expected: 8,
376 actual: frame.len as usize,
377 });
378 }
379
380 let j1_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
382 let j1_deg = bytes_to_i32_be(j1_bytes);
383
384 let j2_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
386 let j2_deg = bytes_to_i32_be(j2_bytes);
387
388 Ok(Self { j1_deg, j2_deg })
389 }
390}
391
392#[derive(Debug, Clone, Copy, Default)]
397pub struct JointFeedback34 {
398 pub j3_deg: i32, pub j4_deg: i32, }
401
402impl JointFeedback34 {
403 pub fn j3_raw(&self) -> i32 {
405 self.j3_deg
406 }
407
408 pub fn j4_raw(&self) -> i32 {
410 self.j4_deg
411 }
412
413 pub fn j3(&self) -> f64 {
415 self.j3_deg as f64 / 1000.0
416 }
417
418 pub fn j4(&self) -> f64 {
420 self.j4_deg as f64 / 1000.0
421 }
422
423 pub fn j3_rad(&self) -> f64 {
425 self.j3() * std::f64::consts::PI / 180.0
426 }
427
428 pub fn j4_rad(&self) -> f64 {
430 self.j4() * std::f64::consts::PI / 180.0
431 }
432}
433
434impl TryFrom<PiperFrame> for JointFeedback34 {
435 type Error = ProtocolError;
436
437 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
438 if frame.id != ID_JOINT_FEEDBACK_34 {
440 return Err(ProtocolError::InvalidCanId { id: frame.id });
441 }
442
443 if frame.len < 8 {
445 return Err(ProtocolError::InvalidLength {
446 expected: 8,
447 actual: frame.len as usize,
448 });
449 }
450
451 let j3_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
453 let j3_deg = bytes_to_i32_be(j3_bytes);
454
455 let j4_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
457 let j4_deg = bytes_to_i32_be(j4_bytes);
458
459 Ok(Self { j3_deg, j4_deg })
460 }
461}
462
463#[derive(Debug, Clone, Copy, Default)]
468pub struct JointFeedback56 {
469 pub j5_deg: i32, pub j6_deg: i32, }
472
473impl JointFeedback56 {
474 pub fn j5_raw(&self) -> i32 {
476 self.j5_deg
477 }
478
479 pub fn j6_raw(&self) -> i32 {
481 self.j6_deg
482 }
483
484 pub fn j5(&self) -> f64 {
486 self.j5_deg as f64 / 1000.0
487 }
488
489 pub fn j6(&self) -> f64 {
491 self.j6_deg as f64 / 1000.0
492 }
493
494 pub fn j5_rad(&self) -> f64 {
496 self.j5() * std::f64::consts::PI / 180.0
497 }
498
499 pub fn j6_rad(&self) -> f64 {
501 self.j6() * std::f64::consts::PI / 180.0
502 }
503}
504
505impl TryFrom<PiperFrame> for JointFeedback56 {
506 type Error = ProtocolError;
507
508 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
509 if frame.id != ID_JOINT_FEEDBACK_56 {
511 return Err(ProtocolError::InvalidCanId { id: frame.id });
512 }
513
514 if frame.len < 8 {
516 return Err(ProtocolError::InvalidLength {
517 expected: 8,
518 actual: frame.len as usize,
519 });
520 }
521
522 let j5_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
524 let j5_deg = bytes_to_i32_be(j5_bytes);
525
526 let j6_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
528 let j6_deg = bytes_to_i32_be(j6_bytes);
529
530 Ok(Self { j5_deg, j6_deg })
531 }
532}
533
534#[derive(Debug, Clone, Copy, Default)]
543pub struct EndPoseFeedback1 {
544 pub x_mm: i32, pub y_mm: i32, }
547
548impl EndPoseFeedback1 {
549 pub fn x_raw(&self) -> i32 {
551 self.x_mm
552 }
553
554 pub fn y_raw(&self) -> i32 {
556 self.y_mm
557 }
558
559 pub fn x(&self) -> f64 {
561 self.x_mm as f64 / 1000.0
562 }
563
564 pub fn y(&self) -> f64 {
566 self.y_mm as f64 / 1000.0
567 }
568}
569
570impl TryFrom<PiperFrame> for EndPoseFeedback1 {
571 type Error = ProtocolError;
572
573 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
574 if frame.id != ID_END_POSE_1 {
576 return Err(ProtocolError::InvalidCanId { id: frame.id });
577 }
578
579 if frame.len < 8 {
581 return Err(ProtocolError::InvalidLength {
582 expected: 8,
583 actual: frame.len as usize,
584 });
585 }
586
587 let x_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
589 let x_mm = bytes_to_i32_be(x_bytes);
590
591 let y_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
593 let y_mm = bytes_to_i32_be(y_bytes);
594
595 Ok(Self { x_mm, y_mm })
596 }
597}
598
599#[derive(Debug, Clone, Copy, Default)]
604pub struct EndPoseFeedback2 {
605 pub z_mm: i32, pub rx_deg: i32, }
608
609impl EndPoseFeedback2 {
610 pub fn z_raw(&self) -> i32 {
612 self.z_mm
613 }
614
615 pub fn rx_raw(&self) -> i32 {
617 self.rx_deg
618 }
619
620 pub fn z(&self) -> f64 {
622 self.z_mm as f64 / 1000.0
623 }
624
625 pub fn rx(&self) -> f64 {
627 self.rx_deg as f64 / 1000.0
628 }
629
630 pub fn rx_rad(&self) -> f64 {
632 self.rx() * std::f64::consts::PI / 180.0
633 }
634}
635
636impl TryFrom<PiperFrame> for EndPoseFeedback2 {
637 type Error = ProtocolError;
638
639 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
640 if frame.id != ID_END_POSE_2 {
642 return Err(ProtocolError::InvalidCanId { id: frame.id });
643 }
644
645 if frame.len < 8 {
647 return Err(ProtocolError::InvalidLength {
648 expected: 8,
649 actual: frame.len as usize,
650 });
651 }
652
653 let z_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
655 let z_mm = bytes_to_i32_be(z_bytes);
656
657 let rx_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
659 let rx_deg = bytes_to_i32_be(rx_bytes);
660
661 Ok(Self { z_mm, rx_deg })
662 }
663}
664
665#[derive(Debug, Clone, Copy, Default)]
670pub struct EndPoseFeedback3 {
671 pub ry_deg: i32, pub rz_deg: i32, }
674
675impl EndPoseFeedback3 {
676 pub fn ry_raw(&self) -> i32 {
678 self.ry_deg
679 }
680
681 pub fn rz_raw(&self) -> i32 {
683 self.rz_deg
684 }
685
686 pub fn ry(&self) -> f64 {
688 self.ry_deg as f64 / 1000.0
689 }
690
691 pub fn rz(&self) -> f64 {
693 self.rz_deg as f64 / 1000.0
694 }
695
696 pub fn ry_rad(&self) -> f64 {
698 self.ry() * std::f64::consts::PI / 180.0
699 }
700
701 pub fn rz_rad(&self) -> f64 {
703 self.rz() * std::f64::consts::PI / 180.0
704 }
705}
706
707impl TryFrom<PiperFrame> for EndPoseFeedback3 {
708 type Error = ProtocolError;
709
710 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
711 if frame.id != ID_END_POSE_3 {
713 return Err(ProtocolError::InvalidCanId { id: frame.id });
714 }
715
716 if frame.len < 8 {
718 return Err(ProtocolError::InvalidLength {
719 expected: 8,
720 actual: frame.len as usize,
721 });
722 }
723
724 let ry_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
726 let ry_deg = bytes_to_i32_be(ry_bytes);
727
728 let rz_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
730 let rz_deg = bytes_to_i32_be(rz_bytes);
731
732 Ok(Self { ry_deg, rz_deg })
733 }
734}
735
736#[derive(Debug, Clone, Copy, Default)]
749pub struct JointDriverHighSpeedFeedback {
750 pub joint_index: u8, pub speed_rad_s: i16, pub current_a: i16, pub position_rad: i32, }
755
756impl JointDriverHighSpeedFeedback {
757 pub const COEFFICIENT_1_3: f64 = 1.18125;
762
763 pub const COEFFICIENT_4_6: f64 = 0.95844;
768
769 pub fn speed_raw(&self) -> i16 {
771 self.speed_rad_s
772 }
773
774 pub fn current_raw(&self) -> i16 {
776 self.current_a
777 }
778
779 pub fn position_raw(&self) -> i32 {
781 self.position_rad
782 }
783
784 pub fn speed(&self) -> f64 {
786 self.speed_rad_s as f64 / 1000.0
787 }
788
789 pub fn current(&self) -> f64 {
793 self.current_a as f64 / 1000.0
794 }
795
796 pub fn position(&self) -> f64 {
798 self.position_rad as f64
799 }
800
801 pub fn position_deg(&self) -> f64 {
803 self.position() * 180.0 / std::f64::consts::PI
804 }
805
806 pub fn torque(&self, current_opt: Option<f64>) -> f64 {
833 let current = current_opt.unwrap_or_else(|| self.current());
834 let coefficient = if self.joint_index <= 3 {
835 Self::COEFFICIENT_1_3
836 } else {
837 Self::COEFFICIENT_4_6
838 };
839 current * coefficient
840 }
841
842 pub fn torque_raw(&self) -> i32 {
859 (self.torque(None) * 1000.0).round() as i32
860 }
861}
862
863impl TryFrom<PiperFrame> for JointDriverHighSpeedFeedback {
864 type Error = ProtocolError;
865
866 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
867 if frame.id < ID_JOINT_DRIVER_HIGH_SPEED_BASE
869 || frame.id > ID_JOINT_DRIVER_HIGH_SPEED_BASE + 5
870 {
871 return Err(ProtocolError::InvalidCanId { id: frame.id });
872 }
873
874 let joint_index = (frame.id - ID_JOINT_DRIVER_HIGH_SPEED_BASE + 1) as u8;
876
877 if frame.len < 8 {
879 return Err(ProtocolError::InvalidLength {
880 expected: 8,
881 actual: frame.len as usize,
882 });
883 }
884
885 let speed_bytes = [frame.data[0], frame.data[1]];
887 let speed_rad_s = bytes_to_i16_be(speed_bytes);
888
889 let current_bytes = [frame.data[2], frame.data[3]];
891 let current_a = bytes_to_i16_be(current_bytes);
892
893 let position_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
895 let position_rad = bytes_to_i32_be(position_bytes);
896
897 Ok(Self {
898 joint_index,
899 speed_rad_s,
900 current_a,
901 position_rad,
902 })
903 }
904}
905
906#[bitsize(8)]
922#[derive(FromBits, DebugBits, Clone, Copy, Default)]
923pub struct DriverStatus {
924 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, }
933
934#[derive(Debug, Clone, Copy, Default)]
941pub struct JointDriverLowSpeedFeedback {
942 pub joint_index: u8, pub voltage: u16, pub driver_temp: i16, pub motor_temp: i8, pub status: DriverStatus, pub bus_current: u16, }
949
950impl JointDriverLowSpeedFeedback {
951 pub fn voltage_raw(&self) -> u16 {
953 self.voltage
954 }
955
956 pub fn driver_temp_raw(&self) -> i16 {
958 self.driver_temp
959 }
960
961 pub fn motor_temp_raw(&self) -> i8 {
963 self.motor_temp
964 }
965
966 pub fn bus_current_raw(&self) -> u16 {
968 self.bus_current
969 }
970
971 pub fn voltage(&self) -> f64 {
973 self.voltage as f64 / 10.0
974 }
975
976 pub fn driver_temp(&self) -> f64 {
978 self.driver_temp as f64
979 }
980
981 pub fn motor_temp(&self) -> f64 {
983 self.motor_temp as f64
984 }
985
986 pub fn bus_current(&self) -> f64 {
988 self.bus_current as f64 / 1000.0
989 }
990}
991
992impl TryFrom<PiperFrame> for JointDriverLowSpeedFeedback {
993 type Error = ProtocolError;
994
995 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
996 let joint_index = (frame.id - ID_JOINT_DRIVER_LOW_SPEED_BASE + 1) as u8;
998 if !(1..=6).contains(&joint_index) {
999 return Err(ProtocolError::InvalidCanId { id: frame.id });
1000 }
1001
1002 if frame.len < 8 {
1004 return Err(ProtocolError::InvalidLength {
1005 expected: 8,
1006 actual: frame.len as usize,
1007 });
1008 }
1009
1010 let voltage_bytes = [frame.data[0], frame.data[1]];
1012 let voltage = u16::from_be_bytes(voltage_bytes);
1013
1014 let driver_temp_bytes = [frame.data[2], frame.data[3]];
1015 let driver_temp = bytes_to_i16_be(driver_temp_bytes);
1016
1017 let motor_temp = frame.data[4] as i8;
1018
1019 let status = DriverStatus::from(u8::new(frame.data[5]));
1021
1022 let bus_current_bytes = [frame.data[6], frame.data[7]];
1023 let bus_current = u16::from_be_bytes(bus_current_bytes);
1024
1025 Ok(Self {
1026 joint_index,
1027 voltage,
1028 driver_temp,
1029 motor_temp,
1030 status,
1031 bus_current,
1032 })
1033 }
1034}
1035
1036#[derive(Debug, Clone, Copy, Default)]
1050pub struct JointEndVelocityAccelFeedback {
1051 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, }
1057
1058impl JointEndVelocityAccelFeedback {
1059 pub fn linear_velocity_raw(&self) -> u16 {
1061 self.linear_velocity_m_s_raw
1062 }
1063
1064 pub fn angular_velocity_raw(&self) -> u16 {
1066 self.angular_velocity_rad_s_raw
1067 }
1068
1069 pub fn linear_accel_raw(&self) -> u16 {
1071 self.linear_accel_m_s2_raw
1072 }
1073
1074 pub fn angular_accel_raw(&self) -> u16 {
1076 self.angular_accel_rad_s2_raw
1077 }
1078
1079 pub fn linear_velocity(&self) -> f64 {
1081 self.linear_velocity_m_s_raw as f64 / 1000.0
1082 }
1083
1084 pub fn angular_velocity(&self) -> f64 {
1086 self.angular_velocity_rad_s_raw as f64 / 1000.0
1087 }
1088
1089 pub fn linear_accel(&self) -> f64 {
1091 self.linear_accel_m_s2_raw as f64 / 1000.0
1092 }
1093
1094 pub fn angular_accel(&self) -> f64 {
1096 self.angular_accel_rad_s2_raw as f64 / 1000.0
1097 }
1098}
1099
1100impl TryFrom<PiperFrame> for JointEndVelocityAccelFeedback {
1101 type Error = ProtocolError;
1102
1103 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
1104 if frame.id < ID_JOINT_END_VELOCITY_ACCEL_BASE
1106 || frame.id > ID_JOINT_END_VELOCITY_ACCEL_BASE + 5
1107 {
1108 return Err(ProtocolError::InvalidCanId { id: frame.id });
1109 }
1110
1111 let joint_index = (frame.id - ID_JOINT_END_VELOCITY_ACCEL_BASE + 1) as u8;
1113
1114 if frame.len < 8 {
1116 return Err(ProtocolError::InvalidLength {
1117 expected: 8,
1118 actual: frame.len as usize,
1119 });
1120 }
1121
1122 let linear_velocity_bytes = [frame.data[0], frame.data[1]];
1124 let linear_velocity_m_s_raw = u16::from_be_bytes(linear_velocity_bytes);
1125
1126 let angular_velocity_bytes = [frame.data[2], frame.data[3]];
1127 let angular_velocity_rad_s_raw = u16::from_be_bytes(angular_velocity_bytes);
1128
1129 let linear_accel_bytes = [frame.data[4], frame.data[5]];
1130 let linear_accel_m_s2_raw = u16::from_be_bytes(linear_accel_bytes);
1131
1132 let angular_accel_bytes = [frame.data[6], frame.data[7]];
1133 let angular_accel_rad_s2_raw = u16::from_be_bytes(angular_accel_bytes);
1134
1135 Ok(Self {
1136 joint_index,
1137 linear_velocity_m_s_raw,
1138 angular_velocity_rad_s_raw,
1139 linear_accel_m_s2_raw,
1140 angular_accel_rad_s2_raw,
1141 })
1142 }
1143}
1144
1145#[bitsize(8)]
1161#[derive(FromBits, DebugBits, Clone, Copy, Default)]
1162pub struct GripperStatus {
1163 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, }
1172
1173#[derive(Debug, Clone, Copy, Default)]
1179pub struct GripperFeedback {
1180 pub travel_mm: i32, pub torque_nm: i16, pub status: GripperStatus, }
1185
1186impl GripperFeedback {
1187 pub fn travel_raw(&self) -> i32 {
1189 self.travel_mm
1190 }
1191
1192 pub fn torque_raw(&self) -> i16 {
1194 self.torque_nm
1195 }
1196
1197 pub fn travel(&self) -> f64 {
1199 self.travel_mm as f64 / 1000.0
1200 }
1201
1202 pub fn torque(&self) -> f64 {
1204 self.torque_nm as f64 / 1000.0
1205 }
1206}
1207
1208impl TryFrom<PiperFrame> for GripperFeedback {
1209 type Error = ProtocolError;
1210
1211 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
1212 if frame.id != ID_GRIPPER_FEEDBACK {
1214 return Err(ProtocolError::InvalidCanId { id: frame.id });
1215 }
1216
1217 if frame.len < 7 {
1219 return Err(ProtocolError::InvalidLength {
1220 expected: 7,
1221 actual: frame.len as usize,
1222 });
1223 }
1224
1225 let travel_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
1227 let travel_mm = bytes_to_i32_be(travel_bytes);
1228
1229 let torque_bytes = [frame.data[4], frame.data[5]];
1230 let torque_nm = bytes_to_i16_be(torque_bytes);
1231
1232 let status = GripperStatus::from(u8::new(frame.data[6]));
1234
1235 Ok(Self {
1236 travel_mm,
1237 torque_nm,
1238 status,
1239 })
1240 }
1241}
1242
1243#[cfg(test)]
1244mod tests {
1245 use super::*;
1246
1247 #[test]
1248 fn test_control_mode_from_u8() {
1249 assert_eq!(ControlMode::from(0x00), ControlMode::Standby);
1250 assert_eq!(ControlMode::from(0x01), ControlMode::CanControl);
1251 assert_eq!(ControlMode::from(0x02), ControlMode::Teach);
1252 assert_eq!(ControlMode::from(0x03), ControlMode::Ethernet);
1253 assert_eq!(ControlMode::from(0x04), ControlMode::Wifi);
1254 assert_eq!(ControlMode::from(0x05), ControlMode::Remote);
1255 assert_eq!(ControlMode::from(0x06), ControlMode::LinkTeach);
1256 assert_eq!(ControlMode::from(0x07), ControlMode::OfflineTrajectory);
1257 assert_eq!(ControlMode::from(0xFF), ControlMode::Standby);
1259 }
1260
1261 #[test]
1262 fn test_robot_status_from_u8() {
1263 assert_eq!(RobotStatus::from(0x00), RobotStatus::Normal);
1264 assert_eq!(RobotStatus::from(0x01), RobotStatus::EmergencyStop);
1265 assert_eq!(RobotStatus::from(0x0F), RobotStatus::ResistorOverTemp);
1266 for i in 0x00..=0x0F {
1268 let status = RobotStatus::from(i);
1269 assert_eq!(status as u8, i);
1270 }
1271 }
1272
1273 #[test]
1274 fn test_move_mode_from_u8() {
1275 assert_eq!(MoveMode::from(0x00), MoveMode::MoveP);
1276 assert_eq!(MoveMode::from(0x01), MoveMode::MoveJ);
1277 assert_eq!(MoveMode::from(0x02), MoveMode::MoveL);
1278 assert_eq!(MoveMode::from(0x03), MoveMode::MoveC);
1279 assert_eq!(MoveMode::from(0x04), MoveMode::MoveM);
1280 assert_eq!(MoveMode::from(0xFF), MoveMode::MoveP);
1282 }
1283
1284 #[test]
1285 fn test_teach_status_from_u8() {
1286 assert_eq!(TeachStatus::from(0x00), TeachStatus::Closed);
1287 assert_eq!(TeachStatus::from(0x01), TeachStatus::StartRecord);
1288 assert_eq!(TeachStatus::from(0x07), TeachStatus::MoveToStart);
1289 for i in 0x00..=0x07 {
1291 let status = TeachStatus::from(i);
1292 assert_eq!(status as u8, i);
1293 }
1294 }
1295
1296 #[test]
1297 fn test_motion_status_from_u8() {
1298 assert_eq!(MotionStatus::from(0x00), MotionStatus::Arrived);
1299 assert_eq!(MotionStatus::from(0x01), MotionStatus::NotArrived);
1300 assert_eq!(MotionStatus::from(0xFF), MotionStatus::NotArrived);
1302 }
1303
1304 #[test]
1305 fn test_enum_values_match_protocol() {
1306 assert_eq!(ControlMode::Standby as u8, 0x00);
1308 assert_eq!(ControlMode::CanControl as u8, 0x01);
1309 assert_eq!(ControlMode::Remote as u8, 0x05);
1310 assert_eq!(ControlMode::LinkTeach as u8, 0x06);
1311
1312 assert_eq!(RobotStatus::Normal as u8, 0x00);
1313 assert_eq!(RobotStatus::EmergencyStop as u8, 0x01);
1314 assert_eq!(RobotStatus::ResistorOverTemp as u8, 0x0F);
1315
1316 assert_eq!(MoveMode::MoveP as u8, 0x00);
1317 assert_eq!(MoveMode::MoveM as u8, 0x04);
1318 }
1319
1320 #[test]
1338 fn test_fault_code_angle_limit_bit_order() {
1339 let byte = 0x01;
1343 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1344 assert!(fault.joint1_limit(), "1号关节应该超限位");
1345 assert!(!fault.joint2_limit(), "2号关节应该正常");
1346 assert!(!fault.joint3_limit(), "3号关节应该正常");
1347 assert!(!fault.joint4_limit(), "4号关节应该正常");
1348 assert!(!fault.joint5_limit(), "5号关节应该正常");
1349 assert!(!fault.joint6_limit(), "6号关节应该正常");
1350
1351 let byte = 0x02;
1355 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1356 assert!(!fault.joint1_limit(), "1号关节应该正常");
1357 assert!(fault.joint2_limit(), "2号关节应该超限位");
1358 assert!(!fault.joint3_limit(), "3号关节应该正常");
1359
1360 let byte = 0x03;
1364 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1365 assert!(fault.joint1_limit(), "1号关节应该超限位");
1366 assert!(fault.joint2_limit(), "2号关节应该超限位");
1367 assert!(!fault.joint3_limit(), "3号关节应该正常");
1368
1369 let byte = 0x3F;
1372 let fault = FaultCodeAngleLimit::from(u8::new(byte));
1373 assert!(fault.joint1_limit(), "1号关节应该超限位");
1374 assert!(fault.joint2_limit(), "2号关节应该超限位");
1375 assert!(fault.joint3_limit(), "3号关节应该超限位");
1376 assert!(fault.joint4_limit(), "4号关节应该超限位");
1377 assert!(fault.joint5_limit(), "5号关节应该超限位");
1378 assert!(fault.joint6_limit(), "6号关节应该超限位");
1379 }
1380
1381 #[test]
1382 fn test_fault_code_angle_limit_encode() {
1383 let mut fault = FaultCodeAngleLimit::from(u8::new(0));
1387 fault.set_joint1_limit(true);
1388 fault.set_joint3_limit(true);
1389 fault.set_joint5_limit(true);
1390
1391 let encoded = u8::from(fault).value();
1393 assert_eq!(encoded, 0x15, "编码值应该是 0x15 (Bit 0, 2, 4 = 1)");
1394
1395 assert!(fault.joint1_limit());
1397 assert!(!fault.joint2_limit());
1398 assert!(fault.joint3_limit());
1399 assert!(!fault.joint4_limit());
1400 assert!(fault.joint5_limit());
1401 assert!(!fault.joint6_limit());
1402 }
1403
1404 #[test]
1405 fn test_fault_code_angle_limit_roundtrip() {
1406 let original_byte = 0x3C;
1410 let fault = FaultCodeAngleLimit::from(u8::new(original_byte));
1411 let encoded = u8::from(fault).value();
1412
1413 assert!(!fault.joint1_limit());
1415 assert!(!fault.joint2_limit());
1416 assert!(fault.joint3_limit());
1417 assert!(fault.joint4_limit());
1418 assert!(fault.joint5_limit());
1419 assert!(fault.joint6_limit());
1420
1421 assert_eq!(encoded & 0b0011_1111, original_byte & 0b0011_1111);
1423 }
1424
1425 #[test]
1426 fn test_fault_code_comm_error_bit_order() {
1427 let byte = 0x01;
1431 let fault = FaultCodeCommError::from(u8::new(byte));
1432 assert!(fault.joint1_comm_error(), "1号关节应该通信异常");
1433 assert!(!fault.joint2_comm_error(), "2号关节应该正常");
1434
1435 let byte = 0x02;
1439 let fault = FaultCodeCommError::from(u8::new(byte));
1440 assert!(!fault.joint1_comm_error(), "1号关节应该正常");
1441 assert!(fault.joint2_comm_error(), "2号关节应该通信异常");
1442 }
1443
1444 #[test]
1445 fn test_fault_code_comm_error_encode() {
1446 let mut fault = FaultCodeCommError::from(u8::new(0));
1450 fault.set_joint2_comm_error(true);
1451 fault.set_joint6_comm_error(true);
1452
1453 let encoded = u8::from(fault).value();
1454 assert_eq!(encoded, 0x22, "编码值应该是 0x22 (Bit 1, 5 = 1)");
1455 }
1456
1457 #[test]
1458 fn test_fault_code_comm_error_all_joints() {
1459 let mut fault = FaultCodeCommError::from(u8::new(0));
1463 fault.set_joint1_comm_error(true);
1464 fault.set_joint2_comm_error(true);
1465 fault.set_joint3_comm_error(true);
1466 fault.set_joint4_comm_error(true);
1467 fault.set_joint5_comm_error(true);
1468 fault.set_joint6_comm_error(true);
1469
1470 let encoded = u8::from(fault).value();
1471 assert_eq!(encoded & 0b0011_1111, 0x3F, "前6位应该都是1");
1472 }
1473
1474 #[test]
1479 fn test_robot_status_feedback_parse() {
1480 let frame = PiperFrame::new_standard(
1481 ID_ROBOT_STATUS as u16,
1482 &[
1483 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0b0011_1111, 0b0000_0000, ],
1492 );
1493
1494 let status = RobotStatusFeedback::try_from(frame).unwrap();
1495
1496 assert_eq!(status.control_mode, ControlMode::CanControl);
1497 assert_eq!(status.robot_status, RobotStatus::Normal);
1498 assert_eq!(status.move_mode, MoveMode::MoveJ);
1499 assert_eq!(status.teach_status, TeachStatus::Closed);
1500 assert_eq!(status.motion_status, MotionStatus::Arrived);
1501 assert_eq!(status.trajectory_point_index, 5);
1502 assert!(status.fault_code_angle_limit.joint1_limit());
1503 assert!(status.fault_code_angle_limit.joint6_limit());
1504 assert!(!status.fault_code_comm_error.joint1_comm_error());
1505 }
1506
1507 #[test]
1508 fn test_robot_status_feedback_invalid_id() {
1509 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
1510 let result = RobotStatusFeedback::try_from(frame);
1511 assert!(result.is_err());
1512 match result.unwrap_err() {
1513 ProtocolError::InvalidCanId { id } => assert_eq!(id, 0x999),
1514 _ => panic!("Expected InvalidCanId error"),
1515 }
1516 }
1517
1518 #[test]
1519 fn test_robot_status_feedback_invalid_length() {
1520 let frame = PiperFrame::new_standard(ID_ROBOT_STATUS as u16, &[0; 4]);
1521 let result = RobotStatusFeedback::try_from(frame);
1522 assert!(result.is_err());
1523 match result.unwrap_err() {
1524 ProtocolError::InvalidLength { expected, actual } => {
1525 assert_eq!(expected, 8);
1526 assert_eq!(actual, 4);
1527 },
1528 _ => panic!("Expected InvalidLength error"),
1529 }
1530 }
1531
1532 #[test]
1533 fn test_robot_status_feedback_all_fields() {
1534 let frame = PiperFrame::new_standard(
1536 ID_ROBOT_STATUS as u16,
1537 &[
1538 0x07, 0x0F, 0x04, 0x07, 0x01, 0xFF, 0b0011_1111, 0b0011_1111, ],
1547 );
1548
1549 let status = RobotStatusFeedback::try_from(frame).unwrap();
1550
1551 assert_eq!(status.control_mode, ControlMode::OfflineTrajectory);
1552 assert_eq!(status.robot_status, RobotStatus::ResistorOverTemp);
1553 assert_eq!(status.move_mode, MoveMode::MoveM);
1554 assert_eq!(status.teach_status, TeachStatus::MoveToStart);
1555 assert_eq!(status.motion_status, MotionStatus::NotArrived);
1556 assert_eq!(status.trajectory_point_index, 0xFF);
1557
1558 assert!(status.fault_code_angle_limit.joint1_limit());
1560 assert!(status.fault_code_angle_limit.joint2_limit());
1561 assert!(status.fault_code_angle_limit.joint3_limit());
1562 assert!(status.fault_code_angle_limit.joint4_limit());
1563 assert!(status.fault_code_angle_limit.joint5_limit());
1564 assert!(status.fault_code_angle_limit.joint6_limit());
1565
1566 assert!(status.fault_code_comm_error.joint1_comm_error());
1568 assert!(status.fault_code_comm_error.joint2_comm_error());
1569 assert!(status.fault_code_comm_error.joint3_comm_error());
1570 assert!(status.fault_code_comm_error.joint4_comm_error());
1571 assert!(status.fault_code_comm_error.joint5_comm_error());
1572 assert!(status.fault_code_comm_error.joint6_comm_error());
1573 }
1574
1575 #[test]
1580 fn test_joint_feedback12_parse() {
1581 let j1_val = 90000i32;
1583 let j2_val = -45000i32;
1584 let mut data = [0u8; 8];
1585 data[0..4].copy_from_slice(&j1_val.to_be_bytes());
1586 data[4..8].copy_from_slice(&j2_val.to_be_bytes());
1587 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &data);
1588
1589 let feedback = JointFeedback12::try_from(frame).unwrap();
1590 assert_eq!(feedback.j1_raw(), 90000);
1591 assert_eq!(feedback.j2_raw(), -45000);
1592 assert!((feedback.j1() - 90.0).abs() < 0.0001);
1593 assert!((feedback.j2() - (-45.0)).abs() < 0.0001);
1594 }
1595
1596 #[test]
1597 fn test_joint_feedback12_physical_conversion() {
1598 let frame = PiperFrame::new_standard(
1600 ID_JOINT_FEEDBACK_12 as u16,
1601 &[
1602 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, ],
1605 );
1606
1607 let feedback = JointFeedback12::try_from(frame).unwrap();
1608 assert_eq!(feedback.j1(), 0.0);
1609 assert!((feedback.j2() - 0.5).abs() < 0.0001);
1610
1611 assert!((feedback.j1_rad() - 0.0).abs() < 0.0001);
1613 assert!((feedback.j2_rad() - (0.5 * std::f64::consts::PI / 180.0)).abs() < 0.0001);
1614 }
1615
1616 #[test]
1617 fn test_joint_feedback12_boundary_values() {
1618 let max_positive = i32::MAX;
1620 let mut data = [0u8; 8];
1621 data[0..4].copy_from_slice(&max_positive.to_be_bytes());
1622 data[4..8].copy_from_slice(&max_positive.to_be_bytes());
1623 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &data);
1624
1625 let feedback = JointFeedback12::try_from(frame).unwrap();
1626 assert_eq!(feedback.j1_raw(), max_positive);
1627 assert_eq!(feedback.j2_raw(), max_positive);
1628
1629 let min_negative = i32::MIN;
1631 let mut data = [0u8; 8];
1632 data[0..4].copy_from_slice(&min_negative.to_be_bytes());
1633 data[4..8].copy_from_slice(&min_negative.to_be_bytes());
1634 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &data);
1635
1636 let feedback = JointFeedback12::try_from(frame).unwrap();
1637 assert_eq!(feedback.j1_raw(), min_negative);
1638 assert_eq!(feedback.j2_raw(), min_negative);
1639 }
1640
1641 #[test]
1642 fn test_joint_feedback12_invalid_id() {
1643 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
1644 let result = JointFeedback12::try_from(frame);
1645 assert!(result.is_err());
1646 match result.unwrap_err() {
1647 ProtocolError::InvalidCanId { id } => assert_eq!(id, 0x999),
1648 _ => panic!("Expected InvalidCanId error"),
1649 }
1650 }
1651
1652 #[test]
1653 fn test_joint_feedback12_invalid_length() {
1654 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_12 as u16, &[0; 4]);
1655 let result = JointFeedback12::try_from(frame);
1656 assert!(result.is_err());
1657 match result.unwrap_err() {
1658 ProtocolError::InvalidLength { expected, actual } => {
1659 assert_eq!(expected, 8);
1660 assert_eq!(actual, 4);
1661 },
1662 _ => panic!("Expected InvalidLength error"),
1663 }
1664 }
1665
1666 #[test]
1667 fn test_joint_feedback34_parse() {
1668 let j3_val = 30000i32;
1670 let j4_val = -60000i32;
1671 let mut data = [0u8; 8];
1672 data[0..4].copy_from_slice(&j3_val.to_be_bytes());
1673 data[4..8].copy_from_slice(&j4_val.to_be_bytes());
1674 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_34 as u16, &data);
1675
1676 let feedback = JointFeedback34::try_from(frame).unwrap();
1677 assert_eq!(feedback.j3_raw(), 30000);
1678 assert_eq!(feedback.j4_raw(), -60000);
1679 assert!((feedback.j3() - 30.0).abs() < 0.0001);
1680 assert!((feedback.j4() - (-60.0)).abs() < 0.0001);
1681 }
1682
1683 #[test]
1684 fn test_joint_feedback56_parse() {
1685 let j5_val = 180000i32;
1687 let j6_val = -90000i32;
1688 let mut data = [0u8; 8];
1689 data[0..4].copy_from_slice(&j5_val.to_be_bytes());
1690 data[4..8].copy_from_slice(&j6_val.to_be_bytes());
1691 let frame = PiperFrame::new_standard(ID_JOINT_FEEDBACK_56 as u16, &data);
1692
1693 let feedback = JointFeedback56::try_from(frame).unwrap();
1694 assert_eq!(feedback.j5_raw(), 180000);
1695 assert_eq!(feedback.j6_raw(), -90000);
1696 assert!((feedback.j5() - 180.0).abs() < 0.0001);
1697 assert!((feedback.j6() - (-90.0)).abs() < 0.0001);
1698 }
1699
1700 #[test]
1701 fn test_joint_feedback_roundtrip() {
1702 let test_cases = vec![
1704 (0i32, 0i32),
1705 (90000i32, -45000i32),
1706 (i32::MAX, i32::MIN),
1707 (180000i32, -90000i32),
1708 ];
1709
1710 for (j1_val, j2_val) in test_cases {
1711 let frame = PiperFrame::new_standard(
1712 ID_JOINT_FEEDBACK_12 as u16,
1713 &[
1714 j1_val.to_be_bytes()[0],
1715 j1_val.to_be_bytes()[1],
1716 j1_val.to_be_bytes()[2],
1717 j1_val.to_be_bytes()[3],
1718 j2_val.to_be_bytes()[0],
1719 j2_val.to_be_bytes()[1],
1720 j2_val.to_be_bytes()[2],
1721 j2_val.to_be_bytes()[3],
1722 ],
1723 );
1724
1725 let feedback = JointFeedback12::try_from(frame).unwrap();
1726 assert_eq!(feedback.j1_raw(), j1_val);
1727 assert_eq!(feedback.j2_raw(), j2_val);
1728 }
1729 }
1730
1731 #[test]
1736 fn test_end_pose_feedback1_parse() {
1737 let x_val = 100000i32;
1739 let y_val = -50000i32;
1740 let mut data = [0u8; 8];
1741 data[0..4].copy_from_slice(&x_val.to_be_bytes());
1742 data[4..8].copy_from_slice(&y_val.to_be_bytes());
1743 let frame = PiperFrame::new_standard(ID_END_POSE_1 as u16, &data);
1744
1745 let feedback = EndPoseFeedback1::try_from(frame).unwrap();
1746 assert_eq!(feedback.x_raw(), 100000);
1747 assert_eq!(feedback.y_raw(), -50000);
1748 assert!((feedback.x() - 100.0).abs() < 0.0001);
1749 assert!((feedback.y() - (-50.0)).abs() < 0.0001);
1750 }
1751
1752 #[test]
1753 fn test_end_pose_feedback1_unit_conversion() {
1754 let x_val = 1234i32; let y_val = -5678i32; let mut data = [0u8; 8];
1758 data[0..4].copy_from_slice(&x_val.to_be_bytes());
1759 data[4..8].copy_from_slice(&y_val.to_be_bytes());
1760 let frame = PiperFrame::new_standard(ID_END_POSE_1 as u16, &data);
1761
1762 let feedback = EndPoseFeedback1::try_from(frame).unwrap();
1763 assert!((feedback.x() - 1.234).abs() < 0.0001);
1764 assert!((feedback.y() - (-5.678)).abs() < 0.0001);
1765 }
1766
1767 #[test]
1768 fn test_end_pose_feedback1_invalid_id() {
1769 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
1770 let result = EndPoseFeedback1::try_from(frame);
1771 assert!(result.is_err());
1772 }
1773
1774 #[test]
1775 fn test_end_pose_feedback2_parse() {
1776 let z_val = 200000i32;
1778 let rx_val = 45000i32;
1779 let mut data = [0u8; 8];
1780 data[0..4].copy_from_slice(&z_val.to_be_bytes());
1781 data[4..8].copy_from_slice(&rx_val.to_be_bytes());
1782 let frame = PiperFrame::new_standard(ID_END_POSE_2 as u16, &data);
1783
1784 let feedback = EndPoseFeedback2::try_from(frame).unwrap();
1785 assert_eq!(feedback.z_raw(), 200000);
1786 assert_eq!(feedback.rx_raw(), 45000);
1787 assert!((feedback.z() - 200.0).abs() < 0.0001);
1788 assert!((feedback.rx() - 45.0).abs() < 0.0001);
1789 assert!((feedback.rx_rad() - (45.0 * std::f64::consts::PI / 180.0)).abs() < 0.0001);
1790 }
1791
1792 #[test]
1793 fn test_end_pose_feedback3_parse() {
1794 let ry_val = 90000i32;
1796 let rz_val = -30000i32;
1797 let mut data = [0u8; 8];
1798 data[0..4].copy_from_slice(&ry_val.to_be_bytes());
1799 data[4..8].copy_from_slice(&rz_val.to_be_bytes());
1800 let frame = PiperFrame::new_standard(ID_END_POSE_3 as u16, &data);
1801
1802 let feedback = EndPoseFeedback3::try_from(frame).unwrap();
1803 assert_eq!(feedback.ry_raw(), 90000);
1804 assert_eq!(feedback.rz_raw(), -30000);
1805 assert!((feedback.ry() - 90.0).abs() < 0.0001);
1806 assert!((feedback.rz() - (-30.0)).abs() < 0.0001);
1807 }
1808
1809 #[test]
1814 fn test_joint_driver_high_speed_feedback_parse() {
1815 let speed_val = 1500i16;
1821 let current_val = 2500u16;
1822 let position_val = 1000000i32; let mut data = [0u8; 8];
1825 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1826 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1827 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1828
1829 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1830 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1831
1832 assert_eq!(feedback.joint_index, 1);
1833 assert_eq!(feedback.speed_raw(), 1500);
1834 assert_eq!(feedback.current_raw(), 2500i16); assert_eq!(feedback.position_raw(), 1000000);
1836 assert!((feedback.speed() - 1.5).abs() < 0.0001);
1837 assert!((feedback.current() - 2.5).abs() < 0.0001);
1838 assert_eq!(feedback.position(), 1000000.0);
1840 }
1841
1842 #[test]
1843 fn test_joint_driver_high_speed_feedback_all_joints() {
1844 for joint_id in 1..=6 {
1846 let can_id = ID_JOINT_DRIVER_HIGH_SPEED_BASE + (joint_id - 1);
1847 let frame = PiperFrame::new_standard(can_id as u16, &[0; 8]);
1848 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1849 assert_eq!(feedback.joint_index, joint_id as u8);
1850 }
1851 }
1852
1853 #[test]
1854 fn test_joint_driver_high_speed_feedback_physical_conversion() {
1855 let speed_val = 3141i16; let current_val = 5000u16; let position_val = 3141592i32; let mut data = [0u8; 8];
1861 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1862 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1863 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1864
1865 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1866 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1867
1868 assert!((feedback.speed() - std::f64::consts::PI).abs() < 0.001);
1869 assert!((feedback.current() - 5.0).abs() < 0.001);
1870 assert_eq!(feedback.position(), position_val as f64);
1872 }
1873
1874 #[test]
1875 fn test_joint_driver_high_speed_feedback_boundary_values() {
1876 let speed_val = i16::MAX;
1879 let current_val = i16::MAX; let position_val = i32::MAX;
1881
1882 let mut data = [0u8; 8];
1883 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1884 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1885 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1886
1887 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1888 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1889
1890 assert_eq!(feedback.speed_raw(), i16::MAX);
1891 assert_eq!(feedback.current_raw(), i16::MAX);
1892 assert_eq!(feedback.position_raw(), i32::MAX);
1893 }
1894
1895 #[test]
1896 fn test_joint_driver_high_speed_feedback_invalid_id() {
1897 let frame = PiperFrame::new_standard(0x250, &[0; 8]); let result = JointDriverHighSpeedFeedback::try_from(frame);
1900 assert!(result.is_err());
1901
1902 let frame = PiperFrame::new_standard(0x257, &[0; 8]); let result = JointDriverHighSpeedFeedback::try_from(frame);
1904 assert!(result.is_err());
1905 }
1906
1907 #[test]
1908 fn test_joint_driver_high_speed_feedback_invalid_length() {
1909 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &[0; 4]);
1910 let result = JointDriverHighSpeedFeedback::try_from(frame);
1911 assert!(result.is_err());
1912 match result.unwrap_err() {
1913 ProtocolError::InvalidLength { expected, actual } => {
1914 assert_eq!(expected, 8);
1915 assert_eq!(actual, 4);
1916 },
1917 _ => panic!("Expected InvalidLength error"),
1918 }
1919 }
1920
1921 #[test]
1922 fn test_joint_driver_high_speed_feedback_negative_speed() {
1923 let speed_val = -1000i16; let current_val = 1000u16; let position_val = 0i32;
1927
1928 let mut data = [0u8; 8];
1929 data[0..2].copy_from_slice(&speed_val.to_be_bytes());
1930 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1931 data[4..8].copy_from_slice(&position_val.to_be_bytes());
1932
1933 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1934 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1935
1936 assert_eq!(feedback.speed_raw(), -1000);
1937 assert!((feedback.speed() - (-1.0)).abs() < 0.0001);
1938 }
1939
1940 #[test]
1941 fn test_joint_driver_high_speed_feedback_torque_joints_1_3() {
1942 let current_val = 1000u16; let mut data = [0u8; 8];
1946 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1947
1948 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
1949 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1950
1951 assert_eq!(feedback.joint_index, 1);
1952 let expected_torque = 1.0 * JointDriverHighSpeedFeedback::COEFFICIENT_1_3; assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1954 assert_eq!(
1955 feedback.torque_raw(),
1956 (expected_torque * 1000.0).round() as i32
1957 );
1958
1959 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 1, &data);
1961 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1962 assert_eq!(feedback.joint_index, 2);
1963 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1964
1965 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 2, &data);
1967 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1968 assert_eq!(feedback.joint_index, 3);
1969 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1970 }
1971
1972 #[test]
1973 fn test_joint_driver_high_speed_feedback_torque_joints_4_6() {
1974 let current_val = 1000u16; let mut data = [0u8; 8];
1978 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
1979
1980 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 3, &data);
1981 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1982
1983 assert_eq!(feedback.joint_index, 4);
1984 let expected_torque = 1.0 * JointDriverHighSpeedFeedback::COEFFICIENT_4_6; assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1986 assert_eq!(
1987 feedback.torque_raw(),
1988 (expected_torque * 1000.0).round() as i32
1989 );
1990
1991 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 4, &data);
1993 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
1994 assert_eq!(feedback.joint_index, 5);
1995 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
1996
1997 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 5, &data);
1999 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
2000 assert_eq!(feedback.joint_index, 6);
2001 assert!((feedback.torque(None) - expected_torque).abs() < 0.0001);
2002 }
2003
2004 #[test]
2005 fn test_joint_driver_high_speed_feedback_torque_with_custom_current() {
2006 let current_val = 2000u16; let custom_current = 2.5; let mut data = [0u8; 8];
2011 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
2012
2013 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
2015 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
2016 let torque_from_feedback = feedback.torque(None);
2017 let expected_from_feedback = 2.0 * JointDriverHighSpeedFeedback::COEFFICIENT_1_3;
2018 assert!((torque_from_feedback - expected_from_feedback).abs() < 0.0001);
2019
2020 let torque_from_custom = feedback.torque(Some(custom_current));
2022 let expected_from_custom = custom_current * JointDriverHighSpeedFeedback::COEFFICIENT_1_3;
2023 assert!((torque_from_custom - expected_from_custom).abs() < 0.0001);
2024
2025 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16 + 3, &data);
2027 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
2028 let torque_from_custom = feedback.torque(Some(custom_current));
2029 let expected_from_custom = custom_current * JointDriverHighSpeedFeedback::COEFFICIENT_4_6;
2030 assert!((torque_from_custom - expected_from_custom).abs() < 0.0001);
2031 }
2032
2033 #[test]
2034 fn test_joint_driver_high_speed_feedback_torque_coefficients() {
2035 assert_eq!(JointDriverHighSpeedFeedback::COEFFICIENT_1_3, 1.18125);
2037 assert_eq!(JointDriverHighSpeedFeedback::COEFFICIENT_4_6, 0.95844);
2038
2039 let current_val = 1000u16; let mut data = [0u8; 8];
2044 data[2..4].copy_from_slice(¤t_val.to_be_bytes());
2045
2046 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_HIGH_SPEED_BASE as u16, &data);
2047 let feedback = JointDriverHighSpeedFeedback::try_from(frame).unwrap();
2048 let torque = feedback.torque(None);
2049 assert!((torque - 1.18125).abs() < 0.0001);
2050 assert_eq!(feedback.torque_raw(), 1181); }
2052
2053 #[test]
2058 fn test_gripper_status_bit_order() {
2059 let byte = 0x41;
2064 let status = GripperStatus::from(u8::new(byte));
2065
2066 assert!(status.voltage_low(), "电压应该过低");
2067 assert!(!status.motor_over_temp(), "电机应该正常");
2068 assert!(status.enabled(), "应该使能(Bit 6 = 1)");
2069 assert!(!status.homed(), "应该没有回零");
2070 }
2071
2072 #[test]
2073 fn test_gripper_status_encode() {
2074 let mut status = GripperStatus::from(u8::new(0));
2075 status.set_voltage_low(true);
2076 status.set_enabled(true);
2077 status.set_homed(true);
2078
2079 let encoded = u8::from(status).value();
2080 assert_eq!(encoded, 0xC1);
2082 }
2083
2084 #[test]
2085 fn test_gripper_feedback_parse() {
2086 let travel_val = 50000i32;
2091 let torque_val = 2500i16;
2092 let status_byte = 0x41u8;
2093
2094 let mut data = [0u8; 8];
2095 data[0..4].copy_from_slice(&travel_val.to_be_bytes());
2096 data[4..6].copy_from_slice(&torque_val.to_be_bytes());
2097 data[6] = status_byte;
2098
2099 let frame = PiperFrame::new_standard(ID_GRIPPER_FEEDBACK as u16, &data);
2100 let feedback = GripperFeedback::try_from(frame).unwrap();
2101
2102 assert_eq!(feedback.travel_raw(), 50000);
2103 assert_eq!(feedback.torque_raw(), 2500);
2104 assert!((feedback.travel() - 50.0).abs() < 0.0001);
2105 assert!((feedback.torque() - 2.5).abs() < 0.0001);
2106 assert!(feedback.status.voltage_low());
2107 assert!(feedback.status.enabled());
2108 }
2109
2110 #[test]
2111 fn test_gripper_feedback_invalid_id() {
2112 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2113 let result = GripperFeedback::try_from(frame);
2114 assert!(result.is_err());
2115 }
2116
2117 #[test]
2118 fn test_gripper_feedback_invalid_length() {
2119 let frame = PiperFrame::new_standard(ID_GRIPPER_FEEDBACK as u16, &[0; 4]);
2120 let result = GripperFeedback::try_from(frame);
2121 assert!(result.is_err());
2122 }
2123
2124 #[test]
2125 fn test_gripper_status_all_flags() {
2126 let mut status = GripperStatus::from(u8::new(0));
2128 status.set_voltage_low(true);
2129 status.set_motor_over_temp(true);
2130 status.set_driver_over_current(true);
2131 status.set_driver_over_temp(true);
2132 status.set_sensor_error(true);
2133 status.set_driver_error(true);
2134 status.set_enabled(true);
2135 status.set_homed(true);
2136
2137 let encoded = u8::from(status).value();
2138 assert_eq!(encoded, 0xFF); }
2140
2141 #[test]
2146 fn test_driver_status_bit_order() {
2147 let byte = 0x55;
2154 let status = DriverStatus::from(u8::new(byte));
2155
2156 assert!(status.voltage_low(), "电源电压应该过低");
2157 assert!(!status.motor_over_temp(), "电机应该正常温度");
2158 assert!(status.driver_over_current(), "驱动器应该过流");
2159 assert!(status.collision_protection(), "碰撞保护应该触发");
2160 assert!(status.enabled(), "驱动器应该使能");
2161 }
2162
2163 #[test]
2164 fn test_driver_status_encode() {
2165 let mut status = DriverStatus::from(u8::new(0));
2166 status.set_voltage_low(true);
2167 status.set_driver_over_current(true);
2168 status.set_collision_protection(true);
2169 status.set_enabled(true);
2170
2171 let encoded = u8::from(status).value();
2172 assert_eq!(encoded, 0x55);
2174 }
2175
2176 #[test]
2177 fn test_joint_driver_low_speed_feedback_parse() {
2178 let voltage_val = 240u16;
2185 let driver_temp_val = 45i16;
2186 let motor_temp_val = 50i8;
2187 let status_byte = 0x44u8; let bus_current_val = 5000u16;
2189
2190 let mut data = [0u8; 8];
2191 data[0..2].copy_from_slice(&voltage_val.to_be_bytes());
2192 data[2..4].copy_from_slice(&driver_temp_val.to_be_bytes());
2193 data[4] = motor_temp_val as u8;
2194 data[5] = status_byte;
2195 data[6..8].copy_from_slice(&bus_current_val.to_be_bytes());
2196
2197 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_LOW_SPEED_BASE as u16, &data);
2198 let feedback = JointDriverLowSpeedFeedback::try_from(frame).unwrap();
2199
2200 assert_eq!(feedback.joint_index, 1);
2201 assert_eq!(feedback.voltage, 240);
2202 assert_eq!(feedback.driver_temp, 45);
2203 assert_eq!(feedback.motor_temp, 50);
2204 assert_eq!(feedback.bus_current, 5000);
2205 assert!(feedback.status.driver_over_current());
2206 assert!(feedback.status.enabled());
2207 }
2208
2209 #[test]
2210 fn test_joint_driver_low_speed_feedback_all_joints() {
2211 for i in 1..=6 {
2213 let id = ID_JOINT_DRIVER_LOW_SPEED_BASE + (i - 1) as u32;
2214 let mut data = [0u8; 8];
2215 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);
2221 let feedback = JointDriverLowSpeedFeedback::try_from(frame).unwrap();
2222 assert_eq!(feedback.joint_index, i);
2223 }
2224 }
2225
2226 #[test]
2227 fn test_joint_driver_low_speed_feedback_conversions() {
2228 let mut data = [0u8; 8];
2229 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);
2236 let feedback = JointDriverLowSpeedFeedback::try_from(frame).unwrap();
2237
2238 assert!((feedback.voltage() - 24.0).abs() < 0.01);
2239 assert!((feedback.driver_temp() - 45.0).abs() < 0.01);
2240 assert!((feedback.motor_temp() - 50.0).abs() < 0.01);
2241 assert!((feedback.bus_current() - 5.0).abs() < 0.001);
2242 }
2243
2244 #[test]
2245 fn test_joint_driver_low_speed_feedback_invalid_id() {
2246 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2247 let result = JointDriverLowSpeedFeedback::try_from(frame);
2248 assert!(result.is_err());
2249 }
2250
2251 #[test]
2252 fn test_joint_driver_low_speed_feedback_invalid_length() {
2253 let frame = PiperFrame::new_standard(ID_JOINT_DRIVER_LOW_SPEED_BASE as u16, &[0; 4]);
2254 let result = JointDriverLowSpeedFeedback::try_from(frame);
2255 assert!(result.is_err());
2256 }
2257
2258 #[test]
2263 fn test_joint_end_velocity_accel_feedback_parse() {
2264 let linear_vel = 1000u16; let angular_vel = 5000u16; let linear_accel = 2000u16; let angular_accel = 3000u16; let mut data = [0u8; 8];
2271 data[0..2].copy_from_slice(&linear_vel.to_be_bytes());
2272 data[2..4].copy_from_slice(&angular_vel.to_be_bytes());
2273 data[4..6].copy_from_slice(&linear_accel.to_be_bytes());
2274 data[6..8].copy_from_slice(&angular_accel.to_be_bytes());
2275
2276 let frame = PiperFrame::new_standard(ID_JOINT_END_VELOCITY_ACCEL_BASE as u16, &data);
2277 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2278
2279 assert_eq!(feedback.joint_index, 1);
2280 assert_eq!(feedback.linear_velocity_m_s_raw, 1000);
2281 assert_eq!(feedback.angular_velocity_rad_s_raw, 5000);
2282 assert_eq!(feedback.linear_accel_m_s2_raw, 2000);
2283 assert_eq!(feedback.angular_accel_rad_s2_raw, 3000);
2284 }
2285
2286 #[test]
2287 fn test_joint_end_velocity_accel_feedback_all_joints() {
2288 for i in 1..=6 {
2290 let id = ID_JOINT_END_VELOCITY_ACCEL_BASE + (i - 1) as u32;
2291 let mut data = [0u8; 8];
2292 data[0..2].copy_from_slice(&1000u16.to_be_bytes());
2293 data[2..4].copy_from_slice(&2000u16.to_be_bytes());
2294 data[4..6].copy_from_slice(&3000u16.to_be_bytes());
2295 data[6..8].copy_from_slice(&4000u16.to_be_bytes());
2296
2297 let frame = PiperFrame::new_standard(id as u16, &data);
2298 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2299 assert_eq!(feedback.joint_index, i);
2300 }
2301 }
2302
2303 #[test]
2304 fn test_joint_end_velocity_accel_feedback_conversions() {
2305 let mut data = [0u8; 8];
2306 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);
2312 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2313
2314 assert!((feedback.linear_velocity() - 1.0).abs() < 0.0001);
2315 assert!((feedback.angular_velocity() - 2.0).abs() < 0.0001);
2316 assert!((feedback.linear_accel() - 3.0).abs() < 0.0001);
2317 assert!((feedback.angular_accel() - 4.0).abs() < 0.0001);
2318 }
2319
2320 #[test]
2321 fn test_joint_end_velocity_accel_feedback_zero() {
2322 let data = [0u8; 8];
2324 let frame = PiperFrame::new_standard(ID_JOINT_END_VELOCITY_ACCEL_BASE as u16, &data);
2325 let feedback = JointEndVelocityAccelFeedback::try_from(frame).unwrap();
2326
2327 assert_eq!(feedback.linear_velocity_m_s_raw, 0);
2328 assert_eq!(feedback.angular_velocity_rad_s_raw, 0);
2329 assert_eq!(feedback.linear_accel_m_s2_raw, 0);
2330 assert_eq!(feedback.angular_accel_rad_s2_raw, 0);
2331 assert!((feedback.linear_velocity() - 0.0).abs() < 0.0001);
2332 assert!((feedback.angular_velocity() - 0.0).abs() < 0.0001);
2333 }
2334
2335 #[test]
2336 fn test_joint_end_velocity_accel_feedback_invalid_id() {
2337 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2338 let result = JointEndVelocityAccelFeedback::try_from(frame);
2339 assert!(result.is_err());
2340 }
2341
2342 #[test]
2343 fn test_joint_end_velocity_accel_feedback_invalid_length() {
2344 let frame = PiperFrame::new_standard(ID_JOINT_END_VELOCITY_ACCEL_BASE as u16, &[0; 7]);
2346 let result = JointEndVelocityAccelFeedback::try_from(frame);
2347 assert!(result.is_err());
2348 }
2349}
2350
2351#[derive(Debug, Clone, Default)]
2360pub struct FirmwareReadFeedback {
2361 pub firmware_data: [u8; 8],
2362}
2363
2364impl FirmwareReadFeedback {
2365 pub fn firmware_data(&self) -> &[u8; 8] {
2367 &self.firmware_data
2368 }
2369
2370 pub fn parse_version_string(accumulated_data: &[u8]) -> Option<String> {
2381 if let Some(version_start) = accumulated_data.windows(3).position(|w| w == b"S-V") {
2383 let version_start = version_start + 3;
2385 let version_end = accumulated_data[version_start..]
2386 .iter()
2387 .position(|&b| b == b'\n' || b == b'\r' || b == 0)
2388 .map(|pos| version_start + pos)
2389 .unwrap_or(accumulated_data.len().min(version_start + 20)); let version_bytes = &accumulated_data[version_start..version_end];
2392 String::from_utf8(version_bytes.to_vec()).ok().map(|s| s.trim().to_string())
2393 } else {
2394 None
2395 }
2396 }
2397}
2398
2399impl TryFrom<PiperFrame> for FirmwareReadFeedback {
2400 type Error = ProtocolError;
2401
2402 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2403 if frame.id != ID_FIRMWARE_READ {
2405 return Err(ProtocolError::InvalidCanId { id: frame.id });
2406 }
2407
2408 if frame.len == 0 {
2410 return Err(ProtocolError::InvalidLength {
2411 expected: 1,
2412 actual: 0,
2413 });
2414 }
2415
2416 let mut firmware_data = [0u8; 8];
2417 let copy_len = (frame.len as usize).min(8);
2418 firmware_data[..copy_len].copy_from_slice(&frame.data[..copy_len]);
2419
2420 Ok(Self { firmware_data })
2421 }
2422}
2423
2424#[cfg(test)]
2425mod firmware_read_tests {
2426 use super::*;
2427
2428 #[test]
2429 fn test_firmware_read_feedback_parse() {
2430 let data = b"S-V1.6-3";
2432 let frame = PiperFrame::new_standard(ID_FIRMWARE_READ as u16, data);
2433 let feedback = FirmwareReadFeedback::try_from(frame).unwrap();
2434
2435 assert_eq!(&feedback.firmware_data[..8], data);
2436 }
2437
2438 #[test]
2439 fn test_firmware_read_feedback_parse_version_string() {
2440 let accumulated_data = b"Some prefix S-V1.6-3\nOther data";
2442 let version = FirmwareReadFeedback::parse_version_string(accumulated_data);
2443 assert_eq!(version, Some("1.6-3".to_string()));
2444 }
2445
2446 #[test]
2447 fn test_firmware_read_feedback_parse_version_string_not_found() {
2448 let accumulated_data = b"Some data without version";
2450 let version = FirmwareReadFeedback::parse_version_string(accumulated_data);
2451 assert_eq!(version, None);
2452 }
2453
2454 #[test]
2455 fn test_firmware_read_feedback_invalid_id() {
2456 let frame = PiperFrame::new_standard(0x999, &[0; 8]);
2457 let result = FirmwareReadFeedback::try_from(frame);
2458 assert!(result.is_err());
2459 }
2460
2461 #[test]
2462 fn test_firmware_read_feedback_empty_data() {
2463 let frame = PiperFrame::new_standard(ID_FIRMWARE_READ as u16, &[]);
2464 let result = FirmwareReadFeedback::try_from(frame);
2465 assert!(result.is_err());
2466 }
2467}
2468
2469#[derive(Debug, Clone, Copy, Default)]
2480pub struct ControlModeCommandFeedback {
2481 pub control_mode: ControlModeCommand,
2482 pub move_mode: MoveMode,
2483 pub speed_percent: u8,
2484 pub mit_mode: MitMode,
2485 pub trajectory_stay_time: u8,
2486 pub install_position: InstallPosition,
2487}
2488
2489impl TryFrom<PiperFrame> for ControlModeCommandFeedback {
2490 type Error = ProtocolError;
2491
2492 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2493 if frame.id != ID_CONTROL_MODE {
2495 return Err(ProtocolError::InvalidCanId { id: frame.id });
2496 }
2497
2498 if frame.len < 6 {
2500 return Err(ProtocolError::InvalidLength {
2501 expected: 6,
2502 actual: frame.len as usize,
2503 });
2504 }
2505
2506 Ok(Self {
2507 control_mode: ControlModeCommand::try_from(frame.data[0])?,
2508 move_mode: MoveMode::from(frame.data[1]),
2509 speed_percent: frame.data[2],
2510 mit_mode: MitMode::try_from(frame.data[3])?,
2511 trajectory_stay_time: frame.data[4],
2512 install_position: InstallPosition::try_from(frame.data[5])?,
2513 })
2514 }
2515}
2516
2517#[derive(Debug, Clone, Copy, Default)]
2522pub struct JointControlFeedback {
2523 pub j1_deg: i32,
2524 pub j2_deg: i32,
2525 pub j3_deg: i32,
2526 pub j4_deg: i32,
2527 pub j5_deg: i32,
2528 pub j6_deg: i32,
2529}
2530
2531impl JointControlFeedback {
2532 pub fn update_from_12(&mut self, feedback: JointControl12Feedback) {
2534 self.j1_deg = feedback.j1_deg;
2535 self.j2_deg = feedback.j2_deg;
2536 }
2537
2538 pub fn update_from_34(&mut self, feedback: JointControl34Feedback) {
2540 self.j3_deg = feedback.j3_deg;
2541 self.j4_deg = feedback.j4_deg;
2542 }
2543
2544 pub fn update_from_56(&mut self, feedback: JointControl56Feedback) {
2546 self.j5_deg = feedback.j5_deg;
2547 self.j6_deg = feedback.j6_deg;
2548 }
2549}
2550
2551#[derive(Debug, Clone, Copy, Default)]
2553pub struct JointControl12Feedback {
2554 pub j1_deg: i32,
2555 pub j2_deg: i32,
2556}
2557
2558impl TryFrom<PiperFrame> for JointControl12Feedback {
2559 type Error = ProtocolError;
2560
2561 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2562 if frame.id != ID_JOINT_CONTROL_12 {
2563 return Err(ProtocolError::InvalidCanId { id: frame.id });
2564 }
2565
2566 if frame.len < 8 {
2567 return Err(ProtocolError::InvalidLength {
2568 expected: 8,
2569 actual: frame.len as usize,
2570 });
2571 }
2572
2573 let j1_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2574 let j1_deg = bytes_to_i32_be(j1_bytes);
2575
2576 let j2_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
2577 let j2_deg = bytes_to_i32_be(j2_bytes);
2578
2579 Ok(Self { j1_deg, j2_deg })
2580 }
2581}
2582
2583#[derive(Debug, Clone, Copy, Default)]
2585pub struct JointControl34Feedback {
2586 pub j3_deg: i32,
2587 pub j4_deg: i32,
2588}
2589
2590impl TryFrom<PiperFrame> for JointControl34Feedback {
2591 type Error = ProtocolError;
2592
2593 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2594 if frame.id != ID_JOINT_CONTROL_34 {
2595 return Err(ProtocolError::InvalidCanId { id: frame.id });
2596 }
2597
2598 if frame.len < 8 {
2599 return Err(ProtocolError::InvalidLength {
2600 expected: 8,
2601 actual: frame.len as usize,
2602 });
2603 }
2604
2605 let j3_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2606 let j3_deg = bytes_to_i32_be(j3_bytes);
2607
2608 let j4_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
2609 let j4_deg = bytes_to_i32_be(j4_bytes);
2610
2611 Ok(Self { j3_deg, j4_deg })
2612 }
2613}
2614
2615#[derive(Debug, Clone, Copy, Default)]
2617pub struct JointControl56Feedback {
2618 pub j5_deg: i32,
2619 pub j6_deg: i32,
2620}
2621
2622impl TryFrom<PiperFrame> for JointControl56Feedback {
2623 type Error = ProtocolError;
2624
2625 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2626 if frame.id != ID_JOINT_CONTROL_56 {
2627 return Err(ProtocolError::InvalidCanId { id: frame.id });
2628 }
2629
2630 if frame.len < 8 {
2631 return Err(ProtocolError::InvalidLength {
2632 expected: 8,
2633 actual: frame.len as usize,
2634 });
2635 }
2636
2637 let j5_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2638 let j5_deg = bytes_to_i32_be(j5_bytes);
2639
2640 let j6_bytes = [frame.data[4], frame.data[5], frame.data[6], frame.data[7]];
2641 let j6_deg = bytes_to_i32_be(j6_bytes);
2642
2643 Ok(Self { j5_deg, j6_deg })
2644 }
2645}
2646
2647#[derive(Debug, Clone, Copy, Default)]
2651pub struct GripperControlFeedback {
2652 pub travel_mm: i32,
2653 pub torque_nm: i16,
2654 pub status_code: u8,
2655 pub set_zero: u8,
2656}
2657
2658impl TryFrom<PiperFrame> for GripperControlFeedback {
2659 type Error = ProtocolError;
2660
2661 fn try_from(frame: PiperFrame) -> Result<Self, Self::Error> {
2662 if frame.id != ID_GRIPPER_CONTROL {
2663 return Err(ProtocolError::InvalidCanId { id: frame.id });
2664 }
2665
2666 if frame.len < 8 {
2667 return Err(ProtocolError::InvalidLength {
2668 expected: 8,
2669 actual: frame.len as usize,
2670 });
2671 }
2672
2673 let travel_bytes = [frame.data[0], frame.data[1], frame.data[2], frame.data[3]];
2674 let travel_mm = bytes_to_i32_be(travel_bytes);
2675
2676 let torque_bytes = [frame.data[4], frame.data[5]];
2677 let torque_nm = bytes_to_i16_be(torque_bytes);
2678
2679 Ok(Self {
2680 travel_mm,
2681 torque_nm,
2682 status_code: frame.data[6],
2683 set_zero: frame.data[7],
2684 })
2685 }
2686}