1use std::sync::atomic::{AtomicU64, Ordering};
6use std::time::Instant;
7
8#[derive(Debug)]
13pub struct FpsStatistics {
14 pub(crate) joint_position_updates: AtomicU64,
16 pub(crate) end_pose_updates: AtomicU64,
17 pub(crate) joint_dynamic_updates: AtomicU64,
18
19 pub(crate) robot_control_updates: AtomicU64,
21 pub(crate) gripper_updates: AtomicU64,
22
23 pub(crate) joint_driver_low_speed_updates: AtomicU64,
25
26 pub(crate) collision_protection_updates: AtomicU64,
28 pub(crate) joint_limit_config_updates: AtomicU64,
29 pub(crate) joint_accel_config_updates: AtomicU64,
30 pub(crate) end_limit_config_updates: AtomicU64,
31 pub(crate) firmware_version_updates: AtomicU64,
32
33 pub(crate) master_slave_control_mode_updates: AtomicU64,
35 pub(crate) master_slave_joint_control_updates: AtomicU64,
36 pub(crate) master_slave_gripper_control_updates: AtomicU64,
37
38 pub(crate) window_start: Instant,
40}
41
42impl FpsStatistics {
43 pub fn new() -> Self {
45 Self {
46 joint_position_updates: AtomicU64::new(0),
47 end_pose_updates: AtomicU64::new(0),
48 joint_dynamic_updates: AtomicU64::new(0),
49 robot_control_updates: AtomicU64::new(0),
50 gripper_updates: AtomicU64::new(0),
51 joint_driver_low_speed_updates: AtomicU64::new(0),
52 collision_protection_updates: AtomicU64::new(0),
53 joint_limit_config_updates: AtomicU64::new(0),
54 joint_accel_config_updates: AtomicU64::new(0),
55 end_limit_config_updates: AtomicU64::new(0),
56 firmware_version_updates: AtomicU64::new(0),
57 master_slave_control_mode_updates: AtomicU64::new(0),
58 master_slave_joint_control_updates: AtomicU64::new(0),
59 master_slave_gripper_control_updates: AtomicU64::new(0),
60 window_start: Instant::now(),
61 }
62 }
63
64 pub fn reset(&mut self) {
70 self.joint_position_updates.store(0, Ordering::Relaxed);
71 self.end_pose_updates.store(0, Ordering::Relaxed);
72 self.joint_dynamic_updates.store(0, Ordering::Relaxed);
73 self.robot_control_updates.store(0, Ordering::Relaxed);
74 self.gripper_updates.store(0, Ordering::Relaxed);
75 self.joint_driver_low_speed_updates.store(0, Ordering::Relaxed);
76 self.collision_protection_updates.store(0, Ordering::Relaxed);
77 self.joint_limit_config_updates.store(0, Ordering::Relaxed);
78 self.joint_accel_config_updates.store(0, Ordering::Relaxed);
79 self.end_limit_config_updates.store(0, Ordering::Relaxed);
80 self.firmware_version_updates.store(0, Ordering::Relaxed);
81 self.master_slave_control_mode_updates.store(0, Ordering::Relaxed);
82 self.master_slave_joint_control_updates.store(0, Ordering::Relaxed);
83 self.master_slave_gripper_control_updates.store(0, Ordering::Relaxed);
84 self.window_start = Instant::now();
85 }
86
87 pub fn calculate_fps(&self) -> FpsResult {
95 let elapsed_secs = self.window_start.elapsed().as_secs_f64();
96
97 let elapsed_secs = elapsed_secs.max(0.001);
99
100 FpsResult {
101 joint_position: self.joint_position_updates.load(Ordering::Relaxed) as f64
102 / elapsed_secs,
103 end_pose: self.end_pose_updates.load(Ordering::Relaxed) as f64 / elapsed_secs,
104 joint_dynamic: self.joint_dynamic_updates.load(Ordering::Relaxed) as f64 / elapsed_secs,
105 robot_control: self.robot_control_updates.load(Ordering::Relaxed) as f64 / elapsed_secs,
106 gripper: self.gripper_updates.load(Ordering::Relaxed) as f64 / elapsed_secs,
107 joint_driver_low_speed: self.joint_driver_low_speed_updates.load(Ordering::Relaxed)
108 as f64
109 / elapsed_secs,
110 collision_protection: self.collision_protection_updates.load(Ordering::Relaxed) as f64
111 / elapsed_secs,
112 joint_limit_config: self.joint_limit_config_updates.load(Ordering::Relaxed) as f64
113 / elapsed_secs,
114 joint_accel_config: self.joint_accel_config_updates.load(Ordering::Relaxed) as f64
115 / elapsed_secs,
116 end_limit_config: self.end_limit_config_updates.load(Ordering::Relaxed) as f64
117 / elapsed_secs,
118 firmware_version: self.firmware_version_updates.load(Ordering::Relaxed) as f64
119 / elapsed_secs,
120 master_slave_control_mode: self
121 .master_slave_control_mode_updates
122 .load(Ordering::Relaxed) as f64
123 / elapsed_secs,
124 master_slave_joint_control: self
125 .master_slave_joint_control_updates
126 .load(Ordering::Relaxed) as f64
127 / elapsed_secs,
128 master_slave_gripper_control: self
129 .master_slave_gripper_control_updates
130 .load(Ordering::Relaxed) as f64
131 / elapsed_secs,
132 }
133 }
134
135 pub fn get_counts(&self) -> FpsCounts {
143 FpsCounts {
144 joint_position: self.joint_position_updates.load(Ordering::Relaxed),
145 end_pose: self.end_pose_updates.load(Ordering::Relaxed),
146 joint_dynamic: self.joint_dynamic_updates.load(Ordering::Relaxed),
147 robot_control: self.robot_control_updates.load(Ordering::Relaxed),
148 gripper: self.gripper_updates.load(Ordering::Relaxed),
149 joint_driver_low_speed: self.joint_driver_low_speed_updates.load(Ordering::Relaxed),
150 collision_protection: self.collision_protection_updates.load(Ordering::Relaxed),
151 joint_limit_config: self.joint_limit_config_updates.load(Ordering::Relaxed),
152 joint_accel_config: self.joint_accel_config_updates.load(Ordering::Relaxed),
153 end_limit_config: self.end_limit_config_updates.load(Ordering::Relaxed),
154 firmware_version: self.firmware_version_updates.load(Ordering::Relaxed),
155 master_slave_control_mode: self
156 .master_slave_control_mode_updates
157 .load(Ordering::Relaxed),
158 master_slave_joint_control: self
159 .master_slave_joint_control_updates
160 .load(Ordering::Relaxed),
161 master_slave_gripper_control: self
162 .master_slave_gripper_control_updates
163 .load(Ordering::Relaxed),
164 }
165 }
166
167 pub fn window_start(&self) -> Instant {
169 self.window_start
170 }
171
172 pub fn elapsed(&self) -> std::time::Duration {
174 self.window_start.elapsed()
175 }
176}
177
178impl Default for FpsStatistics {
179 fn default() -> Self {
180 Self::new()
181 }
182}
183
184#[derive(Debug, Clone, Copy)]
188pub struct FpsResult {
189 pub joint_position: f64,
191 pub end_pose: f64,
193 pub joint_dynamic: f64,
195 pub robot_control: f64,
197 pub gripper: f64,
199 pub joint_driver_low_speed: f64,
201 pub collision_protection: f64,
203 pub joint_limit_config: f64,
205 pub joint_accel_config: f64,
207 pub end_limit_config: f64,
209 pub firmware_version: f64,
211 pub master_slave_control_mode: f64,
213 pub master_slave_joint_control: f64,
215 pub master_slave_gripper_control: f64,
217}
218
219#[derive(Debug, Clone, Copy)]
223pub struct FpsCounts {
224 pub joint_position: u64,
226 pub end_pose: u64,
228 pub joint_dynamic: u64,
230 pub robot_control: u64,
232 pub gripper: u64,
234 pub joint_driver_low_speed: u64,
236 pub collision_protection: u64,
238 pub joint_limit_config: u64,
240 pub joint_accel_config: u64,
242 pub end_limit_config: u64,
244 pub firmware_version: u64,
246 pub master_slave_control_mode: u64,
248 pub master_slave_joint_control: u64,
250 pub master_slave_gripper_control: u64,
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use std::thread;
258 use std::time::Duration;
259
260 #[test]
261 fn test_fps_statistics_new() {
262 let stats = FpsStatistics::new();
263 let counts = stats.get_counts();
264
265 assert_eq!(counts.joint_dynamic, 0);
266 assert_eq!(counts.robot_control, 0);
267 assert_eq!(counts.gripper, 0);
268 }
269
270 #[test]
271 fn test_fps_statistics_reset() {
272 let mut stats = FpsStatistics::new();
273
274 stats.joint_position_updates.fetch_add(100, Ordering::Relaxed);
276 stats.end_pose_updates.fetch_add(50, Ordering::Relaxed);
277
278 let counts_before = stats.get_counts();
280 assert!(counts_before.joint_position > 0);
281
282 stats.reset();
284
285 let counts_after = stats.get_counts();
287 assert_eq!(counts_after.joint_position, 0);
288 assert_eq!(counts_after.end_pose, 0);
289 }
290
291 #[test]
292 fn test_fps_statistics_calculate_fps() {
293 let stats = FpsStatistics::new();
294
295 let fps_initial = stats.calculate_fps();
297 assert_eq!(fps_initial.joint_position, 0.0);
298
299 stats.joint_position_updates.fetch_add(500, Ordering::Relaxed);
301
302 let start = Instant::now();
304 thread::sleep(Duration::from_secs(1));
305 let actual_elapsed = start.elapsed();
306
307 let expected_fps = 500.0;
311 let tolerance = if actual_elapsed.as_secs_f64() > 1.1 {
312 100.0
314 } else {
315 50.0
316 };
317
318 let fps_after = stats.calculate_fps();
319 assert!(
320 (fps_after.joint_position - expected_fps).abs() < tolerance,
321 "Expected FPS ~{:.2}, got {:.2} (actual elapsed: {:.2}s)",
322 expected_fps,
323 fps_after.joint_position,
324 actual_elapsed.as_secs_f64()
325 );
326 }
327
328 #[test]
329 fn test_fps_statistics_get_counts() {
330 let stats = FpsStatistics::new();
331
332 stats.joint_position_updates.fetch_add(100, Ordering::Relaxed);
334 stats.end_pose_updates.fetch_add(150, Ordering::Relaxed);
335 stats.joint_dynamic_updates.fetch_add(200, Ordering::Relaxed);
336 stats.robot_control_updates.fetch_add(50, Ordering::Relaxed);
337
338 let counts = stats.get_counts();
339 assert_eq!(counts.joint_position, 100);
340 assert_eq!(counts.end_pose, 150);
341 assert_eq!(counts.joint_dynamic, 200);
342 assert_eq!(counts.robot_control, 50);
343 assert_eq!(counts.gripper, 0);
344 }
345
346 #[test]
347 fn test_fps_statistics_elapsed() {
348 let stats = FpsStatistics::new();
349
350 let elapsed = stats.elapsed();
352 assert!(elapsed.as_millis() < 100);
353
354 thread::sleep(Duration::from_millis(100));
356
357 let elapsed_after = stats.elapsed();
359 assert!(elapsed_after.as_millis() >= 100);
360 }
361}