use piper_sdk::can::{CanAdapter, CanError, PiperFrame};
use piper_sdk::driver::DriverError;
use piper_sdk::driver::*;
use piper_sdk::protocol::ids::*;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
pub struct MockCanAdapter {
receive_queue: Arc<Mutex<VecDeque<PiperFrame>>>,
sent_frames: Arc<Mutex<Vec<PiperFrame>>>,
}
impl Default for MockCanAdapter {
fn default() -> Self {
Self::new()
}
}
impl MockCanAdapter {
pub fn new() -> Self {
Self {
receive_queue: Arc::new(Mutex::new(VecDeque::new())),
sent_frames: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn queue_frame(&self, frame: PiperFrame) {
self.receive_queue.lock().unwrap().push_back(frame);
}
pub fn queue_frames(&self, frames: Vec<PiperFrame>) {
let mut queue = self.receive_queue.lock().unwrap();
for frame in frames {
queue.push_back(frame);
}
}
pub fn take_sent_frames(&self) -> Vec<PiperFrame> {
std::mem::take(&mut *self.sent_frames.lock().unwrap())
}
pub fn sent_frame_count(&self) -> usize {
self.sent_frames.lock().unwrap().len()
}
pub fn clear_receive_queue(&self) {
self.receive_queue.lock().unwrap().clear();
}
}
impl CanAdapter for MockCanAdapter {
fn send(&mut self, frame: PiperFrame) -> Result<(), CanError> {
self.sent_frames.lock().unwrap().push(frame);
Ok(())
}
fn receive(&mut self) -> Result<PiperFrame, CanError> {
self.receive_queue.lock().unwrap().pop_front().ok_or(CanError::Timeout)
}
}
fn create_joint_feedback_frame(id: u32, j1_deg: f64, j2_deg: f64, timestamp: u32) -> PiperFrame {
let j1_raw = (j1_deg * 1000.0) as i32;
let j2_raw = (j2_deg * 1000.0) as i32;
let mut data = [0u8; 8];
data[0..4].copy_from_slice(&j1_raw.to_be_bytes());
data[4..8].copy_from_slice(&j2_raw.to_be_bytes());
let mut frame = PiperFrame::new_standard(id as u16, &data);
frame.timestamp_us = timestamp as u64;
frame
}
fn create_robot_status_frame() -> PiperFrame {
let mut frame = PiperFrame::new_standard(
ID_ROBOT_STATUS as u16,
&[
0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0b0000_0000, 0b0000_0000, ],
);
frame.timestamp_us = 2000;
frame
}
#[test]
fn test_piper_end_to_end_joint_pos_update() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let frame_2a5 = create_joint_feedback_frame(ID_JOINT_FEEDBACK_12, 10.0, 20.0, 1000);
let frame_2a6 = create_joint_feedback_frame(ID_JOINT_FEEDBACK_34, 30.0, 40.0, 1001);
let frame_2a7 = create_joint_feedback_frame(ID_JOINT_FEEDBACK_56, 50.0, 60.0, 1002);
mock_can_clone.queue_frame(frame_2a5);
mock_can_clone.queue_frame(frame_2a6);
mock_can_clone.queue_frame(frame_2a7);
std::thread::sleep(std::time::Duration::from_millis(200));
let max_attempts = 10;
let mut _found_update = false;
for _ in 0..max_attempts {
let joint_pos = piper.get_joint_position();
if joint_pos.hardware_timestamp_us > 0
|| joint_pos.joint_pos.iter().any(|&v| v.abs() > 0.001)
{
_found_update = true;
assert!(
(joint_pos.joint_pos[0].abs() - 10.0 * std::f64::consts::PI / 180.0).abs() < 0.1
|| joint_pos.joint_pos.iter().any(|&v| v.abs() > 0.001),
"Joint position should be updated"
);
break;
}
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
#[test]
fn test_piper_end_to_end_robot_status_update() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let status_frame = create_robot_status_frame();
mock_can_clone.queue_frame(status_frame);
std::thread::sleep(std::time::Duration::from_millis(200));
let control = piper.get_robot_control();
assert_eq!(control.hardware_timestamp_us, control.hardware_timestamp_us); }
#[test]
fn test_piper_end_to_end_command_send() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let cmd_frame = PiperFrame::new_standard(0x150, &[0x01, 0x02, 0x03]);
piper.send_frame(cmd_frame).unwrap();
std::thread::sleep(std::time::Duration::from_millis(200));
}
#[test]
fn test_piper_end_to_end_full_state_read() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let _joint_pos = piper.get_joint_position();
let _end_pose = piper.get_end_pose();
let _joint = piper.get_joint_dynamic();
let _control = piper.get_robot_control();
let _gripper = piper.get_gripper();
let _motion = piper.get_motion_state();
let _aligned = piper.get_aligned_motion(5000);
let _driver = piper.get_joint_driver_low_speed();
let _limits = piper.get_joint_limit_config().unwrap();
let _accel = piper.get_joint_accel_config().unwrap();
let _end_limits = piper.get_end_limit_config().unwrap();
}
fn create_end_pose_frame(id: u32, val1: f64, val2: f64, timestamp: u32) -> PiperFrame {
let val1_raw = (val1 * 1000.0) as i32;
let val2_raw = (val2 * 1000.0) as i32;
let mut data = [0u8; 8];
data[0..4].copy_from_slice(&val1_raw.to_be_bytes());
data[4..8].copy_from_slice(&val2_raw.to_be_bytes());
let mut frame = PiperFrame::new_standard(id as u16, &data);
frame.timestamp_us = timestamp as u64;
frame
}
fn create_velocity_frame(
joint_index: u8,
speed_rad_s: f64,
current_a: f64,
timestamp: u32,
) -> PiperFrame {
let speed_raw = (speed_rad_s * 1000.0) as i16;
let current_raw = (current_a * 1000.0) as u16;
let mut data = [0u8; 8];
data[0..2].copy_from_slice(&speed_raw.to_be_bytes());
data[2..4].copy_from_slice(¤t_raw.to_be_bytes());
data[4..8].copy_from_slice(&[0; 4]);
let id = ID_JOINT_DRIVER_HIGH_SPEED_BASE + (joint_index as u32 - 1);
let mut frame = PiperFrame::new_standard(id as u16, &data);
frame.timestamp_us = timestamp as u64;
frame
}
#[test]
fn test_piper_end_to_end_complete_frame_groups() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let frames = vec![
create_joint_feedback_frame(ID_JOINT_FEEDBACK_12, 10.0, 20.0, 1000),
create_joint_feedback_frame(ID_JOINT_FEEDBACK_34, 30.0, 40.0, 1001),
create_joint_feedback_frame(ID_JOINT_FEEDBACK_56, 50.0, 60.0, 1002),
];
mock_can_clone.queue_frames(frames);
let end_pose_frames = vec![
create_end_pose_frame(ID_END_POSE_1, 100.0, 200.0, 1003), create_end_pose_frame(ID_END_POSE_2, 300.0, 10.0, 1004), create_end_pose_frame(ID_END_POSE_3, 20.0, 30.0, 1005), ];
mock_can_clone.queue_frames(end_pose_frames);
std::thread::sleep(std::time::Duration::from_millis(300));
let joint_pos = piper.get_joint_position();
let end_pose = piper.get_end_pose();
assert_eq!(joint_pos.joint_pos.len(), 6);
assert_eq!(end_pose.end_pose.len(), 6);
}
#[test]
fn test_piper_end_to_end_velocity_buffer_all_received() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let mut velocity_frames = Vec::new();
for i in 0..6 {
let joint_index = i + 1;
let speed = joint_index as f64;
let current = (joint_index as f64) * 0.1; velocity_frames.push(create_velocity_frame(
joint_index,
speed,
current,
2000 + i as u32,
));
}
mock_can_clone.queue_frames(velocity_frames);
std::thread::sleep(std::time::Duration::from_millis(300));
let joint_dynamic = piper.get_joint_dynamic();
assert_eq!(joint_dynamic.joint_vel.len(), 6);
assert_eq!(joint_dynamic.joint_current.len(), 6);
}
#[test]
fn test_piper_end_to_end_mixed_state_updates() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let joint_pos_frames = vec![
create_joint_feedback_frame(ID_JOINT_FEEDBACK_12, 10.0, 20.0, 3000),
create_joint_feedback_frame(ID_JOINT_FEEDBACK_34, 30.0, 40.0, 3001),
create_joint_feedback_frame(ID_JOINT_FEEDBACK_56, 50.0, 60.0, 3002),
];
mock_can_clone.queue_frames(joint_pos_frames);
let status_frame = create_robot_status_frame();
mock_can_clone.queue_frame(status_frame);
let partial_velocity_frames = vec![
create_velocity_frame(1, 1.0, 0.1, 4000),
create_velocity_frame(2, 2.0, 0.2, 4001),
create_velocity_frame(3, 3.0, 0.3, 4002),
];
mock_can_clone.queue_frames(partial_velocity_frames);
std::thread::sleep(std::time::Duration::from_millis(300));
let _joint_pos = piper.get_joint_position();
let _end_pose = piper.get_end_pose();
let _joint = piper.get_joint_dynamic();
let _control = piper.get_robot_control();
let _gripper = piper.get_gripper();
let _motion = piper.get_motion_state();
}
#[test]
fn test_piper_stress_velocity_partial_loss() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let partial_frames = vec![
create_velocity_frame(1, 1.0, 0.1, 5000),
create_velocity_frame(2, 2.0, 0.2, 5001),
create_velocity_frame(3, 3.0, 0.3, 5002),
];
mock_can_clone.queue_frames(partial_frames);
std::thread::sleep(std::time::Duration::from_millis(500));
let joint_dynamic = piper.get_joint_dynamic();
assert_eq!(joint_dynamic.joint_vel.len(), 6);
assert_eq!(joint_dynamic.joint_current.len(), 6);
}
#[test]
fn test_piper_stress_incomplete_joint_pos_frame_group() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let incomplete_frames = vec![
create_joint_feedback_frame(ID_JOINT_FEEDBACK_12, 10.0, 20.0, 6000),
create_joint_feedback_frame(ID_JOINT_FEEDBACK_34, 30.0, 40.0, 6001),
];
mock_can_clone.queue_frames(incomplete_frames);
std::thread::sleep(std::time::Duration::from_millis(300));
let joint_pos = piper.get_joint_position();
let end_pose = piper.get_end_pose();
assert_eq!(joint_pos.joint_pos.len(), 6);
assert_eq!(end_pose.end_pose.len(), 6);
}
#[test]
fn test_piper_stress_command_channel_full() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let mut sent_count = 0;
for i in 0..15 {
let cmd_frame = PiperFrame::new_standard(0x150 + i, &[i as u8; 4]);
match piper.send_frame(cmd_frame) {
Ok(()) => sent_count += 1,
Err(DriverError::ChannelFull) => {
},
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
assert!(sent_count > 0, "At least some frames should be sent");
std::thread::sleep(std::time::Duration::from_millis(200));
}
#[test]
fn test_piper_stress_mixed_frame_sequence() {
let mock_can = MockCanAdapter::new();
let mock_can_clone = Arc::new(mock_can);
let can_adapter = MockCanAdapter {
receive_queue: mock_can_clone.receive_queue.clone(),
sent_frames: mock_can_clone.sent_frames.clone(),
};
let piper = Piper::new(can_adapter, None).unwrap();
let mut frames = Vec::new();
for i in 0..3 {
let base_time = 7000 + i * 10;
frames.push(create_joint_feedback_frame(
ID_JOINT_FEEDBACK_12,
10.0,
20.0,
base_time,
));
frames.push(create_joint_feedback_frame(
ID_JOINT_FEEDBACK_34,
30.0,
40.0,
base_time + 1,
));
frames.push(create_joint_feedback_frame(
ID_JOINT_FEEDBACK_56,
50.0,
60.0,
base_time + 2,
));
}
for i in 0..2 {
let base_time = 8000 + i * 10;
for j in 1..=6 {
frames.push(create_velocity_frame(
j,
j as f64,
j as f64 * 0.1,
base_time + j as u32,
));
}
}
for i in 0..5 {
let status_frame = create_robot_status_frame();
let mut frame = status_frame;
frame.timestamp_us = 9000 + i;
frames.push(frame);
}
mock_can_clone.queue_frames(frames);
std::thread::sleep(std::time::Duration::from_millis(500));
let _joint_pos = piper.get_joint_position();
let _end_pose = piper.get_end_pose();
let _joint = piper.get_joint_dynamic();
let _control = piper.get_robot_control();
let _gripper = piper.get_gripper();
let _motion = piper.get_motion_state();
}