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,
54 pub negative_limit: bool,
56 pub home_sensor: bool,
58}
59
60impl Cia402PpSnapshot {
61 pub fn pp_control(&self) -> RawControlWord {
65 RawControlWord(self.control_word)
66 }
67
68 pub fn pp_status(&self) -> RawStatusWord {
70 RawStatusWord(self.status_word)
71 }
72
73 pub fn set_pp_control(&mut self, cw: RawControlWord) {
75 self.control_word = cw.raw();
76 }
77
78 pub fn state(&self) -> Cia402State {
82 self.pp_status().state()
83 }
84
85 pub fn cmd_shutdown(&mut self) {
87 let mut cw = self.pp_control();
88 cw.cmd_shutdown();
89 self.set_pp_control(cw);
90 }
91
92 pub fn cmd_switch_on(&mut self) {
94 let mut cw = self.pp_control();
95 cw.cmd_switch_on();
96 self.set_pp_control(cw);
97 }
98
99 pub fn cmd_enable_operation(&mut self) {
101 let mut cw = self.pp_control();
102 cw.cmd_enable_operation();
103 self.set_pp_control(cw);
104 }
105
106 pub fn cmd_disable_operation(&mut self) {
108 let mut cw = self.pp_control();
109 cw.cmd_disable_operation();
110 self.set_pp_control(cw);
111 }
112
113 pub fn cmd_disable_voltage(&mut self) {
115 let mut cw = self.pp_control();
116 cw.cmd_disable_voltage();
117 self.set_pp_control(cw);
118 }
119
120 pub fn cmd_quick_stop(&mut self) {
122 let mut cw = self.pp_control();
123 cw.cmd_quick_stop();
124 self.set_pp_control(cw);
125 }
126
127 pub fn cmd_fault_reset(&mut self) {
129 let mut cw = self.pp_control();
130 cw.cmd_fault_reset();
131 self.set_pp_control(cw);
132 }
133
134 pub fn cmd_clear_fault_reset(&mut self) {
136 let mut cw = self.pp_control();
137 cw.cmd_clear_fault_reset();
138 self.set_pp_control(cw);
139 }
140
141 pub fn ensure_pp_mode(&mut self) {
145 self.modes_of_operation = ModesOfOperation::ProfilePosition.as_i8();
146 }
147
148 pub fn set_target(&mut self, position: i32, velocity: u32, accel: u32, decel: u32) {
150 self.target_position = position;
151 self.profile_velocity = velocity;
152 self.profile_acceleration = accel;
153 self.profile_deceleration = decel;
154 }
155
156 pub fn trigger_move(&mut self) {
158 let mut cw = self.pp_control();
159 cw.set_new_set_point(true);
160 self.set_pp_control(cw);
161 }
162
163 pub fn ack_set_point(&mut self) {
165 let mut cw = self.pp_control();
166 cw.set_new_set_point(false);
167 self.set_pp_control(cw);
168 }
169
170 pub fn set_halt(&mut self, v: bool) {
172 let mut cw = self.pp_control();
173 PpControl::set_halt(&mut cw, v);
174 self.set_pp_control(cw);
175 }
176
177 pub fn set_relative(&mut self, v: bool) {
180 let mut cw = self.pp_control();
181 cw.set_relative(v);
182 self.set_pp_control(cw);
183 }
184
185 pub fn target_reached(&self) -> bool {
189 self.pp_status().pp_target_reached()
190 }
191
192 pub fn set_point_acknowledged(&self) -> bool {
194 self.pp_status().set_point_acknowledge()
195 }
196
197 pub fn following_error(&self) -> bool {
202 self.pp_status().following_error()
203 }
204
205 pub fn internal_limit(&self) -> bool {
208 self.pp_status().internal_limit()
209 }
210
211 pub fn warning(&self) -> bool {
213 self.pp_status().warning()
214 }
215
216 pub fn is_faulted(&self) -> bool {
218 matches!(self.state(), Cia402State::Fault | Cia402State::FaultReactionActive)
219 }
220
221 pub fn ensure_homing_mode(&mut self) {
225 self.modes_of_operation = ModesOfOperation::Homing.as_i8();
226 }
227
228 pub fn trigger_homing(&mut self) {
230 let mut cw = RawControlWord(self.control_word);
231 cw.set_homing_start(true);
232 self.control_word = cw.raw();
233 }
234
235 pub fn clear_homing_start(&mut self) {
237 let mut cw = RawControlWord(self.control_word);
238 cw.set_homing_start(false);
239 self.control_word = cw.raw();
240 }
241
242 pub fn homing_progress(&self) -> HomingProgress {
246 let sw = RawStatusWord(self.status_word);
247 let attained = sw.homing_attained();
248 let reached = sw.homing_target_reached();
249 let error = sw.homing_error();
250
251 if error {
252 HomingProgress::Error
253 } else if attained && reached {
254 HomingProgress::Complete
255 } else if attained {
256 HomingProgress::Attained
257 } else if reached {
258 HomingProgress::Idle
259 } else {
260 HomingProgress::InProgress
261 }
262 }
263
264 pub fn position(&self) -> i32 {
268 self.position_actual
269 }
270
271 pub fn velocity(&self) -> i32 {
273 self.velocity_actual
274 }
275
276 pub fn torque(&self) -> i16 {
278 self.torque_actual
279 }
280
281 pub fn current_mode(&self) -> Option<ModesOfOperation> {
283 ModesOfOperation::from_i8(self.modes_of_operation_display)
284 }
285}
286
287impl AxisView for Cia402PpSnapshot {
292 fn control_word(&self) -> u16 { self.control_word }
293 fn set_control_word(&mut self, word: u16) { self.control_word = word; }
294 fn status_word(&self) -> u16 { self.status_word }
295 fn set_target_position(&mut self, pos: i32) { self.target_position = pos; }
296 fn set_profile_velocity(&mut self, vel: u32) { self.profile_velocity = vel; }
297 fn set_profile_acceleration(&mut self, accel: u32) { self.profile_acceleration = accel; }
298 fn set_profile_deceleration(&mut self, decel: u32) { self.profile_deceleration = decel; }
299 fn set_modes_of_operation(&mut self, mode: i8) { self.modes_of_operation = mode; }
300 fn modes_of_operation_display(&self) -> i8 { self.modes_of_operation_display }
301 fn position_actual(&self) -> i32 { self.position_actual }
302 fn velocity_actual(&self) -> i32 { self.velocity_actual }
303 fn positive_limit_active(&self) -> bool { self.positive_limit }
304 fn negative_limit_active(&self) -> bool { self.negative_limit }
305 fn home_sensor_active(&self) -> bool { self.home_sensor }
306}
307
308#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[test]
317 fn test_snapshot_default() {
318 let s = Cia402PpSnapshot::default();
319 assert_eq!(s.control_word, 0);
320 assert_eq!(s.status_word, 0);
321 assert_eq!(s.target_position, 0);
322 }
323
324 #[test]
325 fn test_snapshot_state_machine() {
326 let mut s = Cia402PpSnapshot { status_word: 0x0040, ..Default::default() };
327 assert_eq!(s.state(), Cia402State::SwitchOnDisabled);
328
329 s.cmd_shutdown();
330 assert_eq!(s.control_word & 0x008F, 0x0006);
331
332 s.cmd_enable_operation();
333 assert_eq!(s.control_word & 0x008F, 0x000F);
334 }
335
336 #[test]
337 fn test_snapshot_pp_motion() {
338 let mut s = Cia402PpSnapshot::default();
339 s.cmd_enable_operation();
340 s.set_target(50_000, 10_000, 2_000, 2_000);
341 s.trigger_move();
342
343 assert_eq!(s.target_position, 50_000);
344 assert_eq!(s.profile_velocity, 10_000);
345 assert!(s.control_word & (1 << 4) != 0);
346
347 s.ack_set_point();
348 assert!(s.control_word & (1 << 4) == 0);
349 }
350
351 #[test]
352 fn test_snapshot_homing() {
353 let mut s = Cia402PpSnapshot::default();
354 s.ensure_homing_mode();
355 assert_eq!(s.modes_of_operation, 6);
356
357 s.cmd_enable_operation();
358 s.trigger_homing();
359 assert!(s.control_word & (1 << 4) != 0);
360
361 s.clear_homing_start();
362 assert!(s.control_word & (1 << 4) == 0);
363 }
364
365 #[test]
366 fn test_snapshot_homing_progress() {
367 let s = Cia402PpSnapshot { status_word: 0x1427, ..Default::default() };
369 assert_eq!(s.homing_progress(), HomingProgress::Complete);
370
371 let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
373 assert_eq!(s.homing_progress(), HomingProgress::Error);
374
375 let s = Cia402PpSnapshot { status_word: 0x0027, ..Default::default() };
377 assert_eq!(s.homing_progress(), HomingProgress::InProgress);
378 }
379
380 #[test]
381 fn test_snapshot_axis_view() {
382 let mut s = Cia402PpSnapshot {
383 status_word: 0x0027,
384 position_actual: 5000,
385 velocity_actual: 1000,
386 modes_of_operation_display: 1,
387 ..Default::default()
388 };
389
390 assert_eq!(AxisView::status_word(&s), 0x0027);
391 assert_eq!(AxisView::position_actual(&s), 5000);
392 assert_eq!(AxisView::velocity_actual(&s), 1000);
393
394 AxisView::set_control_word(&mut s, 0x000F);
395 assert_eq!(s.control_word, 0x000F);
396 AxisView::set_target_position(&mut s, 10_000);
397 assert_eq!(s.target_position, 10_000);
398 }
399
400 #[test]
401 fn test_snapshot_error_status() {
402 let s = Cia402PpSnapshot { status_word: 0x0008, ..Default::default() };
403 assert!(s.is_faulted());
404
405 let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
406 assert!(s.following_error());
407
408 let s = Cia402PpSnapshot { status_word: 0x0827, ..Default::default() };
409 assert!(s.internal_limit());
410 }
411}