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
51impl Cia402PpSnapshot {
52 pub fn pp_control(&self) -> RawControlWord {
56 RawControlWord(self.control_word)
57 }
58
59 pub fn pp_status(&self) -> RawStatusWord {
61 RawStatusWord(self.status_word)
62 }
63
64 pub fn set_pp_control(&mut self, cw: RawControlWord) {
66 self.control_word = cw.raw();
67 }
68
69 pub fn state(&self) -> Cia402State {
73 self.pp_status().state()
74 }
75
76 pub fn cmd_shutdown(&mut self) {
78 let mut cw = self.pp_control();
79 cw.cmd_shutdown();
80 self.set_pp_control(cw);
81 }
82
83 pub fn cmd_switch_on(&mut self) {
85 let mut cw = self.pp_control();
86 cw.cmd_switch_on();
87 self.set_pp_control(cw);
88 }
89
90 pub fn cmd_enable_operation(&mut self) {
92 let mut cw = self.pp_control();
93 cw.cmd_enable_operation();
94 self.set_pp_control(cw);
95 }
96
97 pub fn cmd_disable_operation(&mut self) {
99 let mut cw = self.pp_control();
100 cw.cmd_disable_operation();
101 self.set_pp_control(cw);
102 }
103
104 pub fn cmd_disable_voltage(&mut self) {
106 let mut cw = self.pp_control();
107 cw.cmd_disable_voltage();
108 self.set_pp_control(cw);
109 }
110
111 pub fn cmd_quick_stop(&mut self) {
113 let mut cw = self.pp_control();
114 cw.cmd_quick_stop();
115 self.set_pp_control(cw);
116 }
117
118 pub fn cmd_fault_reset(&mut self) {
120 let mut cw = self.pp_control();
121 cw.cmd_fault_reset();
122 self.set_pp_control(cw);
123 }
124
125 pub fn cmd_clear_fault_reset(&mut self) {
127 let mut cw = self.pp_control();
128 cw.cmd_clear_fault_reset();
129 self.set_pp_control(cw);
130 }
131
132 pub fn ensure_pp_mode(&mut self) {
136 self.modes_of_operation = ModesOfOperation::ProfilePosition.as_i8();
137 }
138
139 pub fn set_target(&mut self, position: i32, velocity: u32, accel: u32, decel: u32) {
141 self.target_position = position;
142 self.profile_velocity = velocity;
143 self.profile_acceleration = accel;
144 self.profile_deceleration = decel;
145 }
146
147 pub fn trigger_move(&mut self) {
149 let mut cw = self.pp_control();
150 cw.set_new_set_point(true);
151 self.set_pp_control(cw);
152 }
153
154 pub fn ack_set_point(&mut self) {
156 let mut cw = self.pp_control();
157 cw.set_new_set_point(false);
158 self.set_pp_control(cw);
159 }
160
161 pub fn set_halt(&mut self, v: bool) {
163 let mut cw = self.pp_control();
164 PpControl::set_halt(&mut cw, v);
165 self.set_pp_control(cw);
166 }
167
168 pub fn set_relative(&mut self, v: bool) {
171 let mut cw = self.pp_control();
172 cw.set_relative(v);
173 self.set_pp_control(cw);
174 }
175
176 pub fn target_reached(&self) -> bool {
180 self.pp_status().pp_target_reached()
181 }
182
183 pub fn set_point_acknowledged(&self) -> bool {
185 self.pp_status().set_point_acknowledge()
186 }
187
188 pub fn following_error(&self) -> bool {
193 self.pp_status().following_error()
194 }
195
196 pub fn internal_limit(&self) -> bool {
199 self.pp_status().internal_limit()
200 }
201
202 pub fn warning(&self) -> bool {
204 self.pp_status().warning()
205 }
206
207 pub fn is_faulted(&self) -> bool {
209 matches!(self.state(), Cia402State::Fault | Cia402State::FaultReactionActive)
210 }
211
212 pub fn ensure_homing_mode(&mut self) {
216 self.modes_of_operation = ModesOfOperation::Homing.as_i8();
217 }
218
219 pub fn trigger_homing(&mut self) {
221 let mut cw = RawControlWord(self.control_word);
222 cw.set_homing_start(true);
223 self.control_word = cw.raw();
224 }
225
226 pub fn clear_homing_start(&mut self) {
228 let mut cw = RawControlWord(self.control_word);
229 cw.set_homing_start(false);
230 self.control_word = cw.raw();
231 }
232
233 pub fn homing_progress(&self) -> HomingProgress {
237 let sw = RawStatusWord(self.status_word);
238 let attained = sw.homing_attained();
239 let reached = sw.homing_target_reached();
240 let error = sw.homing_error();
241
242 if error {
243 HomingProgress::Error
244 } else if attained && reached {
245 HomingProgress::Complete
246 } else if attained {
247 HomingProgress::Attained
248 } else if reached {
249 HomingProgress::Idle
250 } else {
251 HomingProgress::InProgress
252 }
253 }
254
255 pub fn position(&self) -> i32 {
259 self.position_actual
260 }
261
262 pub fn velocity(&self) -> i32 {
264 self.velocity_actual
265 }
266
267 pub fn torque(&self) -> i16 {
269 self.torque_actual
270 }
271
272 pub fn current_mode(&self) -> Option<ModesOfOperation> {
274 ModesOfOperation::from_i8(self.modes_of_operation_display)
275 }
276}
277
278impl AxisView for Cia402PpSnapshot {
283 fn control_word(&self) -> u16 { self.control_word }
284 fn set_control_word(&mut self, word: u16) { self.control_word = word; }
285 fn status_word(&self) -> u16 { self.status_word }
286 fn set_target_position(&mut self, pos: i32) { self.target_position = pos; }
287 fn set_profile_velocity(&mut self, vel: u32) { self.profile_velocity = vel; }
288 fn set_profile_acceleration(&mut self, accel: u32) { self.profile_acceleration = accel; }
289 fn set_profile_deceleration(&mut self, decel: u32) { self.profile_deceleration = decel; }
290 fn set_modes_of_operation(&mut self, mode: i8) { self.modes_of_operation = mode; }
291 fn modes_of_operation_display(&self) -> i8 { self.modes_of_operation_display }
292 fn position_actual(&self) -> i32 { self.position_actual }
293 fn velocity_actual(&self) -> i32 { self.velocity_actual }
294}
295
296#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 fn test_snapshot_default() {
306 let s = Cia402PpSnapshot::default();
307 assert_eq!(s.control_word, 0);
308 assert_eq!(s.status_word, 0);
309 assert_eq!(s.target_position, 0);
310 }
311
312 #[test]
313 fn test_snapshot_state_machine() {
314 let mut s = Cia402PpSnapshot { status_word: 0x0040, ..Default::default() };
315 assert_eq!(s.state(), Cia402State::SwitchOnDisabled);
316
317 s.cmd_shutdown();
318 assert_eq!(s.control_word & 0x008F, 0x0006);
319
320 s.cmd_enable_operation();
321 assert_eq!(s.control_word & 0x008F, 0x000F);
322 }
323
324 #[test]
325 fn test_snapshot_pp_motion() {
326 let mut s = Cia402PpSnapshot::default();
327 s.cmd_enable_operation();
328 s.set_target(50_000, 10_000, 2_000, 2_000);
329 s.trigger_move();
330
331 assert_eq!(s.target_position, 50_000);
332 assert_eq!(s.profile_velocity, 10_000);
333 assert!(s.control_word & (1 << 4) != 0);
334
335 s.ack_set_point();
336 assert!(s.control_word & (1 << 4) == 0);
337 }
338
339 #[test]
340 fn test_snapshot_homing() {
341 let mut s = Cia402PpSnapshot::default();
342 s.ensure_homing_mode();
343 assert_eq!(s.modes_of_operation, 6);
344
345 s.cmd_enable_operation();
346 s.trigger_homing();
347 assert!(s.control_word & (1 << 4) != 0);
348
349 s.clear_homing_start();
350 assert!(s.control_word & (1 << 4) == 0);
351 }
352
353 #[test]
354 fn test_snapshot_homing_progress() {
355 let s = Cia402PpSnapshot { status_word: 0x1427, ..Default::default() };
357 assert_eq!(s.homing_progress(), HomingProgress::Complete);
358
359 let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
361 assert_eq!(s.homing_progress(), HomingProgress::Error);
362
363 let s = Cia402PpSnapshot { status_word: 0x0027, ..Default::default() };
365 assert_eq!(s.homing_progress(), HomingProgress::InProgress);
366 }
367
368 #[test]
369 fn test_snapshot_axis_view() {
370 let mut s = Cia402PpSnapshot {
371 status_word: 0x0027,
372 position_actual: 5000,
373 velocity_actual: 1000,
374 modes_of_operation_display: 1,
375 ..Default::default()
376 };
377
378 assert_eq!(AxisView::status_word(&s), 0x0027);
379 assert_eq!(AxisView::position_actual(&s), 5000);
380 assert_eq!(AxisView::velocity_actual(&s), 1000);
381
382 AxisView::set_control_word(&mut s, 0x000F);
383 assert_eq!(s.control_word, 0x000F);
384 AxisView::set_target_position(&mut s, 10_000);
385 assert_eq!(s.target_position, 10_000);
386 }
387
388 #[test]
389 fn test_snapshot_error_status() {
390 let s = Cia402PpSnapshot { status_word: 0x0008, ..Default::default() };
391 assert!(s.is_faulted());
392
393 let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
394 assert!(s.following_error());
395
396 let s = Cia402PpSnapshot { status_word: 0x0827, ..Default::default() };
397 assert!(s.internal_limit());
398 }
399}