use clap::Parser;
use piper_sdk::driver::{
EndPoseState, FpsResult, GripperState, JointDynamicState, JointPositionState, PiperBuilder,
RobotControlState,
};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
#[derive(Parser, Debug)]
#[command(name = "robot_monitor")]
#[command(about = "机器人实时监控工具")]
struct Args {
#[arg(long)]
interface: Option<String>,
#[arg(long, default_value = "1000000")]
baud_rate: u32,
#[arg(long)]
uds: Option<String>,
}
fn control_mode_to_string(mode: u8) -> &'static str {
match mode {
0x00 => "待机模式",
0x01 => "CAN指令控制模式",
0x02 => "示教模式",
0x03 => "以太网控制模式",
0x04 => "wifi控制模式",
0x05 => "遥控器控制模式",
0x06 => "联动示教输入模式",
0x07 => "离线轨迹模式",
_ => "未知模式",
}
}
fn robot_status_to_string(status: u8) -> &'static str {
match status {
0x00 => "正常",
0x01 => "急停",
0x02 => "无解",
0x03 => "奇异点",
0x04 => "目标角度超过限",
0x05 => "关节通信异常",
0x06 => "关节抱闸未打开",
0x07 => "机械臂发生碰撞",
0x08 => "拖动示教时超速",
0x09 => "关节状态异常",
0x0A => "其它异常",
0x0B => "示教记录",
0x0C => "示教执行",
0x0D => "示教暂停",
0x0E => "主控NTC过温",
0x0F => "释放电阻NTC过温",
_ => "未知状态",
}
}
fn move_mode_to_string(mode: u8) -> &'static str {
match mode {
0x00 => "MOVE P",
0x01 => "MOVE J",
0x02 => "MOVE L",
0x03 => "MOVE C",
0x04 => "MOVE M",
_ => "未知",
}
}
fn motion_status_to_string(status: u8) -> &'static str {
match status {
0x00 => "到达指定点位",
0x01 => "未到达指定点位",
_ => "未知",
}
}
fn print_feedback(
joint_position: &JointPositionState,
end_pose: &EndPoseState,
joint_dynamic: &JointDynamicState,
robot_control: &RobotControlState,
gripper: &GripperState,
fps: &FpsResult,
) {
println!("========================================");
println!("\n状态更新频率 (FPS):");
println!(" 关节位置状态: {:6.2} Hz", fps.joint_position);
println!(" 末端位姿状态: {:6.2} Hz", fps.end_pose);
println!(" 关节动态状态: {:6.2} Hz", fps.joint_dynamic);
println!(" 机器人控制状态: {:6.2} Hz", fps.robot_control);
println!(" 夹爪状态: {:6.2} Hz", fps.gripper);
println!(
"控制模式: {}",
control_mode_to_string(robot_control.control_mode)
);
println!(
"机器人状态: {}",
robot_status_to_string(robot_control.robot_status)
);
println!("MOVE模式: {}", move_mode_to_string(robot_control.move_mode));
println!(
"运动状态: {}",
motion_status_to_string(robot_control.motion_status)
);
println!("\n关节角度 (°):");
for (i, &angle) in joint_position.joint_pos.iter().enumerate() {
let angle_deg = angle.to_degrees();
print!(" J{}: {:7.2}", i + 1, angle_deg);
}
println!();
println!("\n末端位置 (m):");
println!(
" X: {:7.4} Y: {:7.4} Z: {:7.4}",
end_pose.end_pose[0], end_pose.end_pose[1], end_pose.end_pose[2]
);
println!("\n末端姿态 (rad):");
println!(
" Rx: {:7.4} Ry: {:7.4} Rz: {:7.4}",
end_pose.end_pose[3], end_pose.end_pose[4], end_pose.end_pose[5]
);
println!("\n关节速度 (rad/s):");
for (i, &vel) in joint_dynamic.joint_vel.iter().enumerate() {
print!(" J{}: {:7.3}", i + 1, vel);
}
println!();
println!("\n关节电流 (A):");
for (i, ¤t) in joint_dynamic.joint_current.iter().enumerate() {
print!(" J{}: {:7.3}", i + 1, current);
}
println!();
if joint_dynamic.is_complete() {
println!("\n✓ 所有关节数据完整");
} else {
let missing = joint_dynamic.missing_joints();
println!("\n⚠ 缺失关节: {:?}", missing);
}
println!("\n夹爪状态:");
println!(" 行程: {:6.2} mm", gripper.travel);
println!(" 扭矩: {:6.3} N·m", gripper.torque);
println!(
" 是否在运动: {}",
if gripper.is_moving() { "是" } else { "否" }
);
let has_faults = (0..6).any(|i| robot_control.is_angle_limit(i))
|| (0..6).any(|i| robot_control.is_comm_error(i));
if has_faults {
println!("\n⚠ 故障检测:");
for i in 0..6 {
if robot_control.is_angle_limit(i) {
println!(" J{} 角度超限位", i + 1);
}
}
for i in 0..6 {
if robot_control.is_comm_error(i) {
println!(" J{} 通信异常", i + 1);
}
}
}
println!("========================================\n");
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
println!("\n收到退出信号,正在关闭...");
})?;
println!("正在连接到机械臂...");
let builder = if let Some(daemon_addr) = &args.uds {
println!("使用守护进程模式: {}", daemon_addr);
PiperBuilder::new().with_daemon(daemon_addr)
} else if let Some(interface) = &args.interface {
#[cfg(target_os = "linux")]
{
println!(
"使用 CAN 接口: {} (将尝试 SocketCAN,失败时自动切换到 GS-USB)",
interface
);
}
#[cfg(not(target_os = "linux"))]
{
println!("使用设备序列号: {}", interface);
}
PiperBuilder::new().interface(interface)
} else {
#[cfg(target_os = "linux")]
{
println!("使用默认 CAN 接口: can0 (SocketCAN)");
PiperBuilder::new().interface("can0")
}
#[cfg(target_os = "macos")]
{
let default_daemon = "127.0.0.1:18888";
println!("使用默认守护进程模式: {} (UDP)", default_daemon);
PiperBuilder::new().with_daemon(default_daemon)
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
println!("自动扫描 GS-USB 设备...");
PiperBuilder::new()
}
};
let piper = builder
.baud_rate(args.baud_rate) .build()
.map_err(|e| format!("连接失败: {}", e))?;
println!("✓ 已连接到机械臂");
println!("正在监听反馈信息...");
println!("按 Ctrl+C 退出\n");
std::thread::sleep(Duration::from_millis(100));
piper.reset_fps_stats();
let mut fps_window_start = std::time::Instant::now();
let mut iteration = 0u64;
while running.load(Ordering::SeqCst) {
iteration += 1;
let joint_position = piper.get_joint_position();
let end_pose = piper.get_end_pose();
let joint_dynamic = piper.get_joint_dynamic();
let robot_control = piper.get_robot_control();
let gripper = piper.get_gripper();
let fps = piper.get_fps();
println!("[第 {} 次更新]", iteration);
print_feedback(
&joint_position,
&end_pose,
&joint_dynamic,
&robot_control,
&gripper,
&fps,
);
if fps_window_start.elapsed() >= Duration::from_secs(5) {
fps_window_start = std::time::Instant::now();
piper.reset_fps_stats();
}
std::thread::sleep(Duration::from_secs(1));
}
println!("✓ 已关闭");
Ok(())
}