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