autocore_std/motion/
pp_snapshot.rs1use super::axis_view::AxisView;
11use super::cia402::{
12 Cia402Control, Cia402Status, Cia402State,
13 PpControl, PpStatus,
14 HomingControl, HomingStatus,
15 RawControlWord, RawStatusWord,
16 ModesOfOperation,
17};
18
19pub use crate::ethercat::teknic::view::HomingProgress;
22
23#[derive(Debug, Clone, Default)]
34pub struct Cia402PpSnapshot {
35 pub control_word: u16,
37 pub target_position: i32,
38 pub profile_velocity: u32,
39 pub profile_acceleration: u32,
40 pub profile_deceleration: u32,
41 pub modes_of_operation: i8,
42
43 pub status_word: u16,
45 pub position_actual: i32,
46 pub velocity_actual: i32,
47 pub torque_actual: i16,
48 pub modes_of_operation_display: i8,
49
50 pub positive_limit: bool,
53 pub negative_limit: bool,
55 pub home_sensor: bool,
57 pub error_code: u16,
59
60 pub dynamic_max_position_limit: Option<f64>,
64 pub dynamic_min_position_limit: Option<f64>,
66}
67
68impl Cia402PpSnapshot {
69 pub fn pp_control(&self) -> RawControlWord {
73 RawControlWord(self.control_word)
74 }
75
76 pub fn pp_status(&self) -> RawStatusWord {
78 RawStatusWord(self.status_word)
79 }
80
81 pub fn set_pp_control(&mut self, cw: RawControlWord) {
83 self.control_word = cw.raw();
84 }
85
86 pub fn state(&self) -> Cia402State {
90 self.pp_status().state()
91 }
92
93 pub fn cmd_shutdown(&mut self) {
95 let mut cw = self.pp_control();
96 cw.cmd_shutdown();
97 self.set_pp_control(cw);
98 }
99
100 pub fn cmd_switch_on(&mut self) {
102 let mut cw = self.pp_control();
103 cw.cmd_switch_on();
104 self.set_pp_control(cw);
105 }
106
107 pub fn cmd_enable_operation(&mut self) {
109 let mut cw = self.pp_control();
110 cw.cmd_enable_operation();
111 self.set_pp_control(cw);
112 }
113
114 pub fn cmd_disable_operation(&mut self) {
116 let mut cw = self.pp_control();
117 cw.cmd_disable_operation();
118 self.set_pp_control(cw);
119 }
120
121 pub fn cmd_disable_voltage(&mut self) {
123 let mut cw = self.pp_control();
124 cw.cmd_disable_voltage();
125 self.set_pp_control(cw);
126 }
127
128 pub fn cmd_quick_stop(&mut self) {
130 let mut cw = self.pp_control();
131 cw.cmd_quick_stop();
132 self.set_pp_control(cw);
133 }
134
135 pub fn cmd_fault_reset(&mut self) {
137 let mut cw = self.pp_control();
138 cw.cmd_fault_reset();
139 self.set_pp_control(cw);
140 }
141
142 pub fn cmd_clear_fault_reset(&mut self) {
144 let mut cw = self.pp_control();
145 cw.cmd_clear_fault_reset();
146 self.set_pp_control(cw);
147 }
148
149 pub fn ensure_pp_mode(&mut self) {
153 self.modes_of_operation = ModesOfOperation::ProfilePosition.as_i8();
154 }
155
156 pub fn set_target(&mut self, position: i32, velocity: u32, accel: u32, decel: u32) {
158 self.target_position = position;
159 self.profile_velocity = velocity;
160 self.profile_acceleration = accel;
161 self.profile_deceleration = decel;
162 }
163
164 pub fn trigger_move(&mut self) {
166 let mut cw = self.pp_control();
167 cw.set_new_set_point(true);
168 self.set_pp_control(cw);
169 }
170
171 pub fn ack_set_point(&mut self) {
173 let mut cw = self.pp_control();
174 cw.set_new_set_point(false);
175 self.set_pp_control(cw);
176 }
177
178 pub fn set_halt(&mut self, v: bool) {
180 let mut cw = self.pp_control();
181 PpControl::set_halt(&mut cw, v);
182 self.set_pp_control(cw);
183 }
184
185 pub fn set_relative(&mut self, v: bool) {
188 let mut cw = self.pp_control();
189 cw.set_relative(v);
190 self.set_pp_control(cw);
191 }
192
193 pub fn target_reached(&self) -> bool {
197 self.pp_status().pp_target_reached()
198 }
199
200 pub fn set_point_acknowledged(&self) -> bool {
202 self.pp_status().set_point_acknowledge()
203 }
204
205 pub fn following_error(&self) -> bool {
210 self.pp_status().following_error()
211 }
212
213 pub fn internal_limit(&self) -> bool {
216 self.pp_status().internal_limit()
217 }
218
219 pub fn warning(&self) -> bool {
221 self.pp_status().warning()
222 }
223
224 pub fn is_faulted(&self) -> bool {
226 matches!(self.state(), Cia402State::Fault | Cia402State::FaultReactionActive)
227 }
228
229 pub fn ensure_homing_mode(&mut self) {
233 self.modes_of_operation = ModesOfOperation::Homing.as_i8();
234 }
235
236 pub fn trigger_homing(&mut self) {
238 let mut cw = RawControlWord(self.control_word);
239 cw.set_homing_start(true);
240 self.control_word = cw.raw();
241 }
242
243 pub fn clear_homing_start(&mut self) {
245 let mut cw = RawControlWord(self.control_word);
246 cw.set_homing_start(false);
247 self.control_word = cw.raw();
248 }
249
250 pub fn homing_progress(&self) -> HomingProgress {
254 let sw = RawStatusWord(self.status_word);
255 let attained = sw.homing_attained();
256 let reached = sw.homing_target_reached();
257 let error = sw.homing_error();
258
259 if error {
260 HomingProgress::Error
261 } else if attained && reached {
262 HomingProgress::Complete
263 } else if attained {
264 HomingProgress::Attained
265 } else if reached {
266 HomingProgress::Idle
267 } else {
268 HomingProgress::InProgress
269 }
270 }
271
272 pub fn position(&self) -> i32 {
276 self.position_actual
277 }
278
279 pub fn velocity(&self) -> i32 {
281 self.velocity_actual
282 }
283
284 pub fn torque(&self) -> i16 {
286 self.torque_actual
287 }
288
289 pub fn current_mode(&self) -> Option<ModesOfOperation> {
291 ModesOfOperation::from_i8(self.modes_of_operation_display)
292 }
293}
294
295impl AxisView for Cia402PpSnapshot {
300 fn control_word(&self) -> u16 { self.control_word }
301 fn set_control_word(&mut self, word: u16) { self.control_word = word; }
302 fn status_word(&self) -> u16 { self.status_word }
303 fn set_target_position(&mut self, pos: i32) { self.target_position = pos; }
304 fn set_profile_velocity(&mut self, vel: u32) { self.profile_velocity = vel; }
305 fn set_profile_acceleration(&mut self, accel: u32) { self.profile_acceleration = accel; }
306 fn set_profile_deceleration(&mut self, decel: u32) { self.profile_deceleration = decel; }
307 fn set_modes_of_operation(&mut self, mode: i8) { self.modes_of_operation = mode; }
308 fn modes_of_operation_display(&self) -> i8 { self.modes_of_operation_display }
309 fn position_actual(&self) -> i32 { self.position_actual }
310 fn velocity_actual(&self) -> i32 { self.velocity_actual }
311 fn positive_limit_active(&self) -> bool { self.positive_limit }
312 fn negative_limit_active(&self) -> bool { self.negative_limit }
313 fn home_sensor_active(&self) -> bool { self.home_sensor }
314 fn error_code(&self) -> u16 { self.error_code }
315 fn dynamic_max_position_limit(&self) -> Option<f64> { self.dynamic_max_position_limit }
316 fn dynamic_min_position_limit(&self) -> Option<f64> { self.dynamic_min_position_limit }
317}
318
319#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_snapshot_default() {
329 let s = Cia402PpSnapshot::default();
330 assert_eq!(s.control_word, 0);
331 assert_eq!(s.status_word, 0);
332 assert_eq!(s.target_position, 0);
333 }
334
335 #[test]
336 fn test_snapshot_state_machine() {
337 let mut s = Cia402PpSnapshot { status_word: 0x0040, ..Default::default() };
338 assert_eq!(s.state(), Cia402State::SwitchOnDisabled);
339
340 s.cmd_shutdown();
341 assert_eq!(s.control_word & 0x008F, 0x0006);
342
343 s.cmd_enable_operation();
344 assert_eq!(s.control_word & 0x008F, 0x000F);
345 }
346
347 #[test]
348 fn test_snapshot_pp_motion() {
349 let mut s = Cia402PpSnapshot::default();
350 s.cmd_enable_operation();
351 s.set_target(50_000, 10_000, 2_000, 2_000);
352 s.trigger_move();
353
354 assert_eq!(s.target_position, 50_000);
355 assert_eq!(s.profile_velocity, 10_000);
356 assert!(s.control_word & (1 << 4) != 0);
357
358 s.ack_set_point();
359 assert!(s.control_word & (1 << 4) == 0);
360 }
361
362 #[test]
363 fn test_snapshot_homing() {
364 let mut s = Cia402PpSnapshot::default();
365 s.ensure_homing_mode();
366 assert_eq!(s.modes_of_operation, 6);
367
368 s.cmd_enable_operation();
369 s.trigger_homing();
370 assert!(s.control_word & (1 << 4) != 0);
371
372 s.clear_homing_start();
373 assert!(s.control_word & (1 << 4) == 0);
374 }
375
376 #[test]
377 fn test_snapshot_homing_progress() {
378 let s = Cia402PpSnapshot { status_word: 0x1427, ..Default::default() };
380 assert_eq!(s.homing_progress(), HomingProgress::Complete);
381
382 let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
384 assert_eq!(s.homing_progress(), HomingProgress::Error);
385
386 let s = Cia402PpSnapshot { status_word: 0x0027, ..Default::default() };
388 assert_eq!(s.homing_progress(), HomingProgress::InProgress);
389 }
390
391 #[test]
392 fn test_snapshot_axis_view() {
393 let mut s = Cia402PpSnapshot {
394 status_word: 0x0027,
395 position_actual: 5000,
396 velocity_actual: 1000,
397 modes_of_operation_display: 1,
398 ..Default::default()
399 };
400
401 assert_eq!(AxisView::status_word(&s), 0x0027);
402 assert_eq!(AxisView::position_actual(&s), 5000);
403 assert_eq!(AxisView::velocity_actual(&s), 1000);
404
405 AxisView::set_control_word(&mut s, 0x000F);
406 assert_eq!(s.control_word, 0x000F);
407 AxisView::set_target_position(&mut s, 10_000);
408 assert_eq!(s.target_position, 10_000);
409 }
410
411 #[test]
412 fn test_snapshot_error_status() {
413 let s = Cia402PpSnapshot { status_word: 0x0008, ..Default::default() };
414 assert!(s.is_faulted());
415
416 let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
417 assert!(s.following_error());
418
419 let s = Cia402PpSnapshot { status_word: 0x0827, ..Default::default() };
420 assert!(s.internal_limit());
421 }
422}