Skip to main content

piper_client/
heartbeat.rs

1//! Heartbeat - 后台心跳机制
2//!
3//! **⚠️ 注意:此功能已禁用**
4//!
5//! 经过硬件验证,PiPER 机械臂**没有看门狗机制**,不需要定期发送心跳信号。
6//! 此模块保留仅用于未来可能的扩展需求。
7//!
8//! # 历史设计目标(已废弃)
9//!
10//! - **超时保护**: 即使主线程冻结,心跳仍然发送
11//! - **低开销**: 50Hz 发送频率,不影响控制性能
12//! - **优雅关闭**: 可以安全停止心跳线程
13//!
14//! # 工作原理(已废弃)
15//!
16//! ~~硬件通常有看门狗定时器(Watchdog Timer),如果在规定时间内
17//! 没有收到命令或心跳,会自动失能以保护安全。~~
18//!
19//! **实际情况**:PiPER 机械臂没有看门狗机制,不需要定期信号。
20
21use std::sync::Arc;
22use std::sync::atomic::{AtomicBool, Ordering};
23use std::thread;
24use std::time::Duration;
25
26use crate::types::{Result, RobotError};
27use piper_driver::Piper as RobotPiper;
28
29/// Heartbeat 配置
30#[derive(Debug, Clone)]
31pub struct HeartbeatConfig {
32    /// 心跳间隔(毫秒)
33    pub interval_ms: u64,
34    /// 是否启用心跳(默认启用)
35    pub enabled: bool,
36}
37
38impl Default for HeartbeatConfig {
39    fn default() -> Self {
40        HeartbeatConfig {
41            interval_ms: 20, // 50Hz
42            enabled: false,  // 已禁用:机械臂没有看门狗机制,不需要心跳包
43        }
44    }
45}
46
47/// 心跳管理器
48///
49/// 在后台线程中定期发送心跳帧。
50pub struct HeartbeatManager {
51    handle: Option<thread::JoinHandle<()>>,
52    shutdown: Arc<AtomicBool>,
53}
54
55impl HeartbeatManager {
56    /// 启动心跳线程
57    ///
58    /// # 参数
59    ///
60    /// - `can_sender`: CAN 发送接口
61    /// - `config`: 心跳配置
62    ///
63    /// # 示例
64    ///
65    /// ```rust,ignore
66    /// # use piper_client::client::heartbeat::*;
67    /// # use std::sync::Arc;
68    /// # fn example(robot: Arc<RobotPiper>) {
69    /// let heartbeat = HeartbeatManager::start(
70    ///     robot,
71    ///     HeartbeatConfig::default(),
72    /// );
73    /// # }
74    /// ```
75    pub fn start(robot: Arc<RobotPiper>, config: HeartbeatConfig) -> Self {
76        if !config.enabled {
77            // 心跳被禁用
78            return HeartbeatManager {
79                handle: None,
80                shutdown: Arc::new(AtomicBool::new(true)),
81            };
82        }
83
84        let shutdown = Arc::new(AtomicBool::new(false));
85        let shutdown_clone = shutdown.clone();
86
87        let handle = thread::spawn(move || {
88            Self::heartbeat_loop(robot, config, shutdown_clone);
89        });
90
91        HeartbeatManager {
92            handle: Some(handle),
93            shutdown,
94        }
95    }
96
97    /// 心跳循环
98    fn heartbeat_loop(robot: Arc<RobotPiper>, config: HeartbeatConfig, shutdown: Arc<AtomicBool>) {
99        let interval = Duration::from_millis(config.interval_ms);
100
101        while !shutdown.load(Ordering::Relaxed) {
102            // 发送心跳帧
103            if let Err(_e) = Self::send_heartbeat(&robot) {
104                // 心跳发送失败(通常是通信问题)
105                // 记录警告但继续尝试
106                tracing::warn!("Heartbeat failed: {}", _e);
107            }
108
109            thread::sleep(interval);
110        }
111    }
112
113    /// 发送心跳帧
114    ///
115    /// **⚠️ 已废弃**:机械臂没有看门狗机制,不需要心跳包。
116    /// 此方法保留仅用于未来可能的扩展需求。
117    ///
118    /// # 错误
119    ///
120    /// 目前总是返回错误,因为没有定义有效的心跳帧协议。
121    /// 如果未来需要实现心跳,应该:
122    /// 1. 与硬件厂商协商定义专用的心跳帧 ID
123    /// 2. 或者使用协议中定义的合法指令(如定期发送 0x151)
124    fn send_heartbeat(_robot: &Arc<RobotPiper>) -> Result<()> {
125        // ❌ 不实现:机械臂没有看门狗机制
126        Err(RobotError::ConfigError(
127            "Heartbeat is not supported: robot does not have a watchdog mechanism".to_string(),
128        ))
129    }
130
131    /// 优雅关闭心跳线程
132    ///
133    /// 设置关闭标志并等待线程结束。
134    pub fn shutdown(mut self) {
135        self.shutdown.store(true, Ordering::Relaxed);
136        if let Some(handle) = self.handle.take() {
137            let _ = handle.join();
138        }
139    }
140
141    /// 检查心跳线程是否在运行
142    pub fn is_running(&self) -> bool {
143        !self.shutdown.load(Ordering::Relaxed)
144    }
145}
146
147impl Drop for HeartbeatManager {
148    fn drop(&mut self) {
149        self.shutdown.store(true, Ordering::Relaxed);
150        if let Some(handle) = self.handle.take() {
151            let _ = handle.join();
152        }
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    // 注意:heartbeat 测试需要真实的 robot 实例,应该在集成测试中完成
160    // 这里只测试基本逻辑
161
162    #[test]
163    fn test_heartbeat_config_default() {
164        let config = HeartbeatConfig::default();
165        // ✅ 根据 HEARTBEAT_ANALYSIS_REPORT.md,默认应该禁用(机械臂没有看门狗机制)
166        assert!(!config.enabled);
167        assert_eq!(config.interval_ms, 20);
168    }
169
170    #[test]
171    fn test_heartbeat_config_disabled() {
172        let config = HeartbeatConfig {
173            enabled: false,
174            interval_ms: 10,
175        };
176        assert!(!config.enabled);
177    }
178}