Skip to main content

piper_client/
observer.rs

1//! Observer - 状态观察器(View 模式)
2//!
3//! 直接持有 `driver::Piper` 引用,零拷贝、零延迟地读取底层状态。
4//! 不再使用缓存层,避免数据延迟和锁竞争。
5//!
6//! # 设计目标
7//!
8//! - **零延迟**: 直接从 `driver::Piper` 读取,无缓存层
9//! - **零拷贝**: 使用 ArcSwap 的 wait-free 读取
10//! - **类型安全**: 返回强类型单位(Rad, RadPerSecond, NewtonMeter)
11//! - **逻辑一致性**: 提供 `snapshot()` 方法保证时间一致性
12//!
13//! # 使用示例
14//!
15//! ```rust,no_run
16//! # use piper_client::observer::Observer;
17//! # use piper_client::types::*;
18//! # fn example(observer: Observer) -> Result<()> {
19//! // 读取关节位置
20//! let positions = observer.joint_positions();
21//! println!("J1 position: {}", positions[Joint::J1].to_deg());
22//!
23//! // 使用 snapshot 获取时间一致的数据(推荐用于控制算法)
24//! let snapshot = observer.snapshot();
25//! println!("Position: {:?}, Velocity: {:?}", snapshot.position, snapshot.velocity);
26//!
27//! // 克隆 Observer 用于另一个线程
28//! let observer2 = observer.clone();
29//! std::thread::spawn(move || {
30//!     loop {
31//!         let snapshot = observer2.snapshot();
32//!         // ... 监控状态 ...
33//!     }
34//! });
35//! # Ok(())
36//! # }
37//! ```
38
39use std::sync::Arc;
40use std::time::Instant;
41
42use crate::types::*;
43use piper_driver::Piper as RobotPiper;
44use piper_protocol::constants::*;
45
46/// 状态观察器(只读接口,View 模式)
47///
48/// 直接持有 `driver::Piper` 引用,零拷贝、零延迟地读取底层状态。
49/// 不再使用缓存层,避免数据延迟和锁竞争。
50#[derive(Clone)]
51pub struct Observer {
52    /// Driver 实例(直接持有,零拷贝)
53    driver: Arc<RobotPiper>,
54}
55
56impl Observer {
57    /// 创建新的 Observer
58    ///
59    /// **注意:** 此方法通常不直接调用,Observer 应该通过 `Piper` 状态机的 `observer()` 方法获取。
60    /// 此方法为 `pub` 以支持内部测试和性能基准测试。
61    ///
62    /// **基准测试:** 为了支持性能基准测试,此方法在 benches 中也可访问。
63    pub fn new(driver: Arc<RobotPiper>) -> Self {
64        Observer { driver }
65    }
66
67    /// 获取运动快照(推荐用于控制算法)
68    ///
69    /// 此方法尽可能快地连续读取多个相关状态,减少时间偏斜。
70    /// 即使底层是分帧更新的,此方法也能提供逻辑上最一致的数据。
71    ///
72    /// # 性能
73    ///
74    /// - 延迟:~20ns(连续调用 3 次 ArcSwap::load)
75    /// - 无锁竞争(ArcSwap 是 Wait-Free 的)
76    ///
77    /// # 推荐使用场景
78    ///
79    /// - 高频控制算法(>100Hz)
80    /// - 阻抗控制、力矩控制等需要时间一致性的算法
81    pub fn snapshot(&self) -> MotionSnapshot {
82        // 在读取之前记录时间戳,更准确地反映"读取动作发生"的时刻
83        let timestamp = Instant::now();
84
85        // 连续读取,减少中间被抢占的概率
86        let pos = self.driver.get_joint_position();
87        let dyn_state = self.driver.get_joint_dynamic();
88
89        MotionSnapshot {
90            position: JointArray::new(pos.joint_pos.map(Rad)),
91            // ✅ 使用类型安全的单位
92            velocity: JointArray::new(dyn_state.joint_vel.map(RadPerSecond)),
93            torque: JointArray::new(dyn_state.get_all_torques().map(NewtonMeter)),
94            timestamp, // 使用读取前的时间戳
95        }
96    }
97
98    /// 获取关节位置(独立读取,可能与其他状态有时间偏斜)
99    ///
100    /// # 注意
101    ///
102    /// 如果需要与其他状态(如速度、力矩)保持时间一致性,
103    /// 请使用 `snapshot()` 方法。
104    pub fn joint_positions(&self) -> JointArray<Rad> {
105        let raw_pos = self.driver.get_joint_position();
106        JointArray::new(raw_pos.joint_pos.map(Rad))
107    }
108
109    /// 获取关节速度(独立读取,可能与其他状态有时间偏斜)
110    ///
111    /// # 注意
112    ///
113    /// 如果需要与其他状态(如位置、力矩)保持时间一致性,
114    /// 请使用 `snapshot()` 方法。
115    ///
116    /// # 返回值
117    ///
118    /// 返回 `JointArray<RadPerSecond>`,保持类型安全。
119    pub fn joint_velocities(&self) -> JointArray<RadPerSecond> {
120        let dyn_state = self.driver.get_joint_dynamic();
121        // ✅ 使用类型安全的单位
122        JointArray::new(dyn_state.joint_vel.map(RadPerSecond))
123    }
124
125    /// 获取关节力矩(独立读取,可能与其他状态有时间偏斜)
126    ///
127    /// # 注意
128    ///
129    /// 如果需要与其他状态(如位置、速度)保持时间一致性,
130    /// 请使用 `snapshot()` 方法。
131    pub fn joint_torques(&self) -> JointArray<NewtonMeter> {
132        let dyn_state = self.driver.get_joint_dynamic();
133        JointArray::new(dyn_state.get_all_torques().map(NewtonMeter))
134    }
135
136    /// 获取夹爪状态
137    pub fn gripper_state(&self) -> GripperState {
138        let gripper = self.driver.get_gripper();
139        GripperState {
140            position: (gripper.travel / GRIPPER_POSITION_SCALE).clamp(0.0, 1.0),
141            effort: (gripper.torque / GRIPPER_FORCE_SCALE).clamp(0.0, 1.0),
142            enabled: gripper.is_enabled(),
143        }
144    }
145
146    /// 获取夹爪位置 (0.0-1.0)
147    pub fn gripper_position(&self) -> f64 {
148        self.gripper_state().position
149    }
150
151    /// 获取夹爪力度 (0.0-1.0)
152    pub fn gripper_effort(&self) -> f64 {
153        self.gripper_state().effort
154    }
155
156    /// 检查夹爪是否使能
157    pub fn is_gripper_enabled(&self) -> bool {
158        self.driver.get_gripper().is_enabled()
159    }
160
161    /// 获取使能掩码(Bit 0-5 对应 J1-J6)
162    pub fn joint_enabled_mask(&self) -> u8 {
163        let driver_state = self.driver.get_joint_driver_low_speed();
164        driver_state.driver_enabled_mask
165    }
166
167    /// 检查指定关节是否使能
168    pub fn is_joint_enabled(&self, joint_index: usize) -> bool {
169        let driver_state = self.driver.get_joint_driver_low_speed();
170        (driver_state.driver_enabled_mask & (1 << joint_index)) != 0
171    }
172
173    /// 获取末端位姿(独立读取,可能与其他状态有时间偏斜)
174    ///
175    /// # 返回值
176    ///
177    /// 返回 `EndPoseState`,包含:
178    /// - `end_pose`: [X, Y, Z, Rx, Ry, Rz]
179    ///   - X, Y, Z: 位置(米)
180    ///   - Rx, Ry, Rz: 姿态角(弧度)
181    ///
182    /// # 示例
183    ///
184    /// ```rust,no_run
185    /// # use piper_client::observer::Observer;
186    /// # fn example(observer: Observer) {
187    /// let end_pose = observer.end_pose();
188    /// println!("Position: X={:.4}, Y={:.4}, Z={:.4}",
189    ///     end_pose.end_pose[0], end_pose.end_pose[1], end_pose.end_pose[2]);
190    /// # }
191    /// ```
192    pub fn end_pose(&self) -> piper_driver::state::EndPoseState {
193        self.driver.get_end_pose()
194    }
195
196    /// 检查是否全部使能
197    pub fn is_all_enabled(&self) -> bool {
198        self.joint_enabled_mask() == 0b111111
199    }
200
201    /// 检查是否全部失能
202    pub fn is_all_disabled(&self) -> bool {
203        self.joint_enabled_mask() == 0
204    }
205
206    /// 检查是否部分使能
207    pub fn is_partially_enabled(&self) -> bool {
208        let mask = self.joint_enabled_mask();
209        mask != 0 && mask != 0b111111
210    }
211
212    /// 检查机械臂是否使能(兼容旧 API)
213    ///
214    /// 如果所有关节都使能,返回 `true`。
215    pub fn is_arm_enabled(&self) -> bool {
216        self.is_all_enabled()
217    }
218
219    /// 获取单个关节的状态
220    ///
221    /// 返回 (position, velocity, torque) 元组。
222    /// **注意**:此方法独立读取,可能与其他状态有时间偏斜。
223    /// 如需时间一致性,请使用 `snapshot()` 方法。
224    pub fn joint_state(&self, joint: Joint) -> (Rad, RadPerSecond, NewtonMeter) {
225        let pos = self.driver.get_joint_position();
226        let dyn_state = self.driver.get_joint_dynamic();
227        (
228            Rad(pos.joint_pos[joint.index()]),
229            RadPerSecond(dyn_state.joint_vel[joint.index()]),
230            NewtonMeter(dyn_state.get_torque(joint.index())),
231        )
232    }
233
234    // ============================================================
235    // 连接监控 API
236    // ============================================================
237
238    /// 检查机器人是否仍在响应
239    ///
240    /// 如果在超时窗口内收到反馈,返回 `true`。
241    /// 这可用于检测机器人是否断电、CAN 线缆断开或固件崩溃。
242    ///
243    /// # 示例
244    ///
245    /// ```rust,ignore
246    /// # use piper_client::observer::Observer;
247    /// # fn example(observer: Observer) {
248    /// if observer.is_connected() {
249    ///     println!("Robot is still responding");
250    /// } else {
251    ///     println!("Robot connection lost!");
252    /// }
253    /// # }
254    /// ```
255    pub fn is_connected(&self) -> bool {
256        self.driver.is_connected()
257    }
258
259    /// 获取自上次反馈以来的时间
260    ///
261    /// 返回自上次成功处理 CAN 帧以来的时间。
262    /// 可用于连接质量监控或诊断。
263    ///
264    /// # 示例
265    ///
266    /// ```rust,ignore
267    /// # use piper_client::observer::Observer;
268    /// # fn example(observer: Observer) {
269    /// let age = observer.connection_age();
270    /// if age.as_millis() > 100 {
271    ///     println!("Connection is degrading: {}ms since last feedback", age.as_millis());
272    /// }
273    /// # }
274    /// ```
275    pub fn connection_age(&self) -> std::time::Duration {
276        self.driver.connection_age()
277    }
278
279    /// 获取碰撞保护级别
280    ///
281    /// 返回6个关节的当前碰撞防护等级(0~8,等级0代表不检测碰撞)。
282    ///
283    /// # 返回
284    ///
285    /// 返回 `[J1, J2, J3, J4, J5, J6]` 的碰撞防护等级
286    ///
287    /// # 示例
288    ///
289    /// ```rust,ignore
290    /// # use piper_client::Observer;
291    /// # fn example(observer: Observer) {
292    /// let levels = observer.collision_protection_levels();
293    /// println!("J1-J6 碰撞保护级别: {:?}", levels);
294    ///
295    /// // 检查某个关节的保护等级
296    /// if levels[0] == 0 {
297    ///     println!("J1 未启用碰撞保护");
298    /// }
299    /// # }
300    /// ```
301    pub fn collision_protection_levels(&self) -> [u8; 6] {
302        self.driver
303            .get_collision_protection()
304            .map(|state| state.protection_levels)
305            .unwrap_or([0; 6]) // 如果读取失败,返回默认值
306    }
307}
308
309/// 夹爪状态
310#[derive(Debug, Clone, Copy, PartialEq)]
311pub struct GripperState {
312    /// 位置 (0.0-1.0)
313    pub position: f64,
314    /// 力度 (0.0-1.0)
315    pub effort: f64,
316    /// 使能状态
317    pub enabled: bool,
318}
319
320impl Default for GripperState {
321    fn default() -> Self {
322        GripperState {
323            position: 0.0,
324            effort: 0.0,
325            enabled: false,
326        }
327    }
328}
329
330/// 运动快照(逻辑原子性)
331///
332/// **设计说明:**
333/// - 使用 `#[non_exhaustive]` 允许未来非破坏性地添加字段
334/// - 例如:加速度、数据有效性标志等衍生数据
335#[derive(Debug, Clone)]
336#[non_exhaustive] // ✅ 允许未来非破坏性地添加字段
337pub struct MotionSnapshot {
338    /// 关节位置
339    pub position: JointArray<Rad>,
340    /// 关节速度(✅ 使用类型安全的单位)
341    pub velocity: JointArray<RadPerSecond>,
342    /// 关节力矩
343    pub torque: JointArray<NewtonMeter>,
344    /// 读取时间戳(用于调试)
345    pub timestamp: Instant,
346}
347
348// 确保 Send + Sync
349unsafe impl Send for Observer {}
350unsafe impl Sync for Observer {}
351
352#[cfg(test)]
353mod tests {
354    use super::*;
355
356    // 注意:单元测试中创建真实的 robot 实例需要真实的 CAN 适配器
357    // 这里只测试类型和基本逻辑,集成测试会测试完整功能
358
359    // 注意:这些测试需要真实的 robot 实例,应该在集成测试中完成
360    // 这里只测试类型系统和基本逻辑
361
362    #[test]
363    fn test_motion_snapshot_structure() {
364        // 测试 MotionSnapshot 结构
365        let snapshot = MotionSnapshot {
366            position: JointArray::splat(Rad(0.0)),
367            velocity: JointArray::splat(RadPerSecond(0.0)),
368            torque: JointArray::splat(NewtonMeter(0.0)),
369            timestamp: Instant::now(),
370        };
371
372        // ✅ 验证速度单位类型正确
373        let _: RadPerSecond = snapshot.velocity[Joint::J1];
374        let _: JointArray<Rad> = snapshot.position;
375        let _: JointArray<NewtonMeter> = snapshot.torque;
376    }
377
378    #[test]
379    fn test_gripper_state_structure() {
380        let gripper = GripperState {
381            position: 0.5,
382            effort: 0.7,
383            enabled: true,
384        };
385
386        // 验证归一化范围
387        assert!(gripper.position >= 0.0 && gripper.position <= 1.0);
388        assert!(gripper.effort >= 0.0 && gripper.effort <= 1.0);
389    }
390
391    #[test]
392    fn test_send_sync() {
393        fn assert_send_sync<T: Send + Sync>() {}
394        assert_send_sync::<Observer>();
395        assert_send_sync::<GripperState>();
396        assert_send_sync::<MotionSnapshot>();
397    }
398}