use piper_sdk::driver::*;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
#[test]
fn test_joint_position_state_concurrent_read() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 10;
let reads_per_thread = 1000;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..reads_per_thread {
let new_state = JointPositionState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
joint_pos: [i as f64; 6],
frame_valid_mask: 0b111,
};
ctx_writer.joint_position.store(Arc::new(new_state));
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
let mut last_timestamp = 0u64;
for _ in 0..reads_per_thread {
let state = ctx_reader.joint_position.load();
if state.hardware_timestamp_us >= last_timestamp {
last_timestamp = state.hardware_timestamp_us;
}
assert!(state.joint_pos.len() == 6);
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_end_pose_state_concurrent_read() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 10;
let reads_per_thread = 1000;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..reads_per_thread {
let new_state = EndPoseState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
end_pose: [i as f64; 6],
frame_valid_mask: 0b111,
};
ctx_writer.end_pose.store(Arc::new(new_state));
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
for _ in 0..reads_per_thread {
let state = ctx_reader.end_pose.load();
assert!(state.end_pose.len() == 6);
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_joint_driver_low_speed_state_concurrent_read() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 10;
let reads_per_thread = 1000;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..reads_per_thread {
let new_state = JointDriverLowSpeedState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
motor_temps: [i as f32; 6],
driver_temps: [i as f32 + 10.0; 6],
joint_voltage: [24.0 + i as f32 * 0.1; 6],
joint_bus_current: [1.0 + i as f32 * 0.01; 6],
driver_voltage_low_mask: if i % 2 == 0 { 0b0000_0001 } else { 0 },
driver_motor_over_temp_mask: 0,
driver_over_current_mask: 0,
driver_over_temp_mask: 0,
driver_collision_protection_mask: 0,
driver_error_mask: 0,
driver_enabled_mask: 0b111111,
driver_stall_protection_mask: 0,
hardware_timestamps: [i as u64 * 100; 6],
system_timestamps: [i as u64 * 200; 6],
valid_mask: 0b111111,
};
ctx_writer.joint_driver_low_speed.store(Arc::new(new_state));
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
for _ in 0..reads_per_thread {
let state = ctx_reader.joint_driver_low_speed.load();
assert!(state.motor_temps.len() == 6);
assert!(state.driver_temps.len() == 6);
let _ = state.is_voltage_low(0);
let _ = state.is_enabled(0);
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_robot_control_state_concurrent_read() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 10;
let reads_per_thread = 1000;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..reads_per_thread {
let new_state = RobotControlState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
control_mode: (i % 256) as u8,
robot_status: ((i + 1) % 256) as u8,
move_mode: ((i + 2) % 256) as u8,
teach_status: ((i + 3) % 256) as u8,
motion_status: ((i + 4) % 256) as u8,
trajectory_point_index: ((i + 5) % 256) as u8,
fault_angle_limit_mask: if i % 2 == 0 { 0b0000_0001 } else { 0 },
fault_comm_error_mask: 0,
is_enabled: i % 2 == 0,
feedback_counter: (i % 256) as u8,
};
ctx_writer.robot_control.store(Arc::new(new_state));
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
for _ in 0..reads_per_thread {
let state = ctx_reader.robot_control.load();
let _ = state.is_angle_limit(0);
let _ = state.is_comm_error(0);
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_gripper_state_concurrent_read() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 10;
let reads_per_thread = 1000;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..reads_per_thread {
let new_state = GripperState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
travel: i as f64 * 0.1,
torque: i as f64 * 0.01,
status_code: (i % 256) as u8,
last_travel: (i - 1) as f64 * 0.1,
};
ctx_writer.gripper.store(Arc::new(new_state));
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
for _ in 0..reads_per_thread {
let state = ctx_reader.gripper.load();
assert!(state.travel >= 0.0);
let _ = state.is_voltage_low();
let _ = state.is_moving();
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_capture_motion_snapshot_concurrent() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 10;
let calls_per_thread = 1000;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..calls_per_thread {
let new_joint_pos = JointPositionState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
joint_pos: [i as f64; 6],
frame_valid_mask: 0b111,
};
ctx_writer.joint_position.store(Arc::new(new_joint_pos));
let new_end_pose = EndPoseState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
end_pose: [i as f64; 6],
frame_valid_mask: 0b111,
};
ctx_writer.end_pose.store(Arc::new(new_end_pose));
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
for _ in 0..calls_per_thread {
let snapshot = ctx_reader.capture_motion_snapshot();
assert!(snapshot.joint_position.joint_pos.len() == 6);
assert!(snapshot.end_pose.end_pose.len() == 6);
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_collision_protection_state_concurrent_read() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 10;
let reads_per_thread = 100;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..reads_per_thread {
if let Ok(mut state) = ctx_writer.collision_protection.write() {
state.hardware_timestamp_us = i as u64 * 1000;
state.system_timestamp_us = i as u64 * 2000;
state.protection_levels = [(i % 9) as u8; 6];
state.protection_levels[0] = (i % 9) as u8;
}
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
for _ in 0..reads_per_thread {
if let Ok(state) = ctx_reader.collision_protection.read() {
assert!(state.protection_levels.len() == 6);
assert!(state.protection_levels[0] <= 8);
}
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_multiple_states_concurrent_read() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 5;
let reads_per_thread = 500;
let ctx_writer = ctx.clone();
let writer_handle = thread::spawn(move || {
for i in 0..reads_per_thread {
let new_joint_pos = JointPositionState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
joint_pos: [i as f64; 6],
frame_valid_mask: 0b111,
};
ctx_writer.joint_position.store(Arc::new(new_joint_pos));
let new_robot_control = RobotControlState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
control_mode: (i % 256) as u8,
robot_status: ((i + 1) % 256) as u8,
move_mode: ((i + 2) % 256) as u8,
teach_status: ((i + 3) % 256) as u8,
motion_status: ((i + 4) % 256) as u8,
trajectory_point_index: ((i + 5) % 256) as u8,
fault_angle_limit_mask: 0,
fault_comm_error_mask: 0,
is_enabled: i % 2 == 0,
feedback_counter: (i % 256) as u8,
};
ctx_writer.robot_control.store(Arc::new(new_robot_control));
let new_gripper = GripperState {
hardware_timestamp_us: i as u64 * 1000,
system_timestamp_us: i as u64 * 2000,
travel: i as f64 * 0.1,
torque: i as f64 * 0.01,
status_code: (i % 256) as u8,
last_travel: (i - 1) as f64 * 0.1,
};
ctx_writer.gripper.store(Arc::new(new_gripper));
thread::yield_now();
}
});
let mut reader_handles = Vec::new();
for _ in 0..num_threads {
let ctx_reader = ctx.clone();
let handle = thread::spawn(move || {
for _ in 0..reads_per_thread {
let joint_pos = ctx_reader.joint_position.load();
let robot_control = ctx_reader.robot_control.load();
let gripper = ctx_reader.gripper.load();
assert!(joint_pos.joint_pos.len() == 6);
let _ = robot_control.control_mode;
assert!(gripper.travel >= 0.0);
thread::yield_now();
}
});
reader_handles.push(handle);
}
writer_handle.join().unwrap();
for handle in reader_handles {
handle.join().unwrap();
}
}
#[test]
fn test_no_deadlock() {
let ctx = Arc::new(PiperContext::new());
let num_threads = 20;
let duration = Duration::from_secs(2);
let start_time = std::time::Instant::now();
let mut handles = Vec::new();
for i in 0..num_threads {
let ctx_clone = ctx.clone();
let handle = thread::spawn(move || {
let mut counter = 0;
while start_time.elapsed() < duration {
if i % 2 == 0 {
let _ = ctx_clone.joint_position.load();
let _ = ctx_clone.robot_control.load();
let _ = ctx_clone.gripper.load();
} else {
let new_state = JointPositionState {
hardware_timestamp_us: counter,
system_timestamp_us: counter * 2,
joint_pos: [counter as f64; 6],
frame_valid_mask: 0b111,
};
ctx_clone.joint_position.store(Arc::new(new_state));
counter += 1;
}
thread::yield_now();
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}