Skip to main content

autocore_std/motion/
move_to_load.rs

1use crate::fb::StateMachine;
2use super::axis_view::AxisHandle;
3
4/// Move To Load function block.
5///
6/// Moves an axis towards a target load (e.g., from a load cell) and stops
7/// as quickly as possible once the edge of that load is reached.
8/// 
9/// - If `current_load > target_load`, it moves in the negative direction.
10/// - If `current_load < target_load`, it moves in the positive direction.
11///
12/// A hysteresis value (minimum 1.0) is used to handle noise spikes. The axis
13/// will halt as soon as `current_load` crosses the `target_load +/- hysteresis` threshold.
14///
15/// If the axis reaches the `position_limit` before the load is achieved, the block
16/// halts the axis and enters an error state.
17#[derive(Debug, Clone)]
18pub struct MoveToLoad {
19    /// Output: operation completed successfully.
20    pub done: bool,
21    /// Output: block is currently executing motion.
22    pub active: bool,
23    /// Output: operation failed. Check `state.error_code`.
24    pub error: bool,
25    /// Internal state machine.
26    pub state: StateMachine,
27    
28    moving_negative: bool,
29}
30
31impl Default for MoveToLoad {
32    fn default() -> Self {
33        Self {
34            done: false,
35            active: false,
36            error: false,
37            state: StateMachine::new(),
38            moving_negative: false,
39        }
40    }
41}
42
43impl MoveToLoad {
44    pub fn new() -> Self {
45        Self::default()
46    }
47
48    /// Abort the operation immediately.
49    pub fn abort(&mut self, axis: &mut impl AxisHandle) {
50        axis.halt();
51        self.error = true;
52        self.active = false;
53        self.state.set_error(1, "Abort called");
54        self.state.index = 100;
55    }
56
57    /// Execute the function block.
58    ///
59    /// - `axis`: The handle to the PP axis.
60    /// - `execute`: Set to true to start the move. On a falling edge, the axis halts.
61    /// - `target_load`: The target load to reach.
62    /// - `current_load`: The raw, instantaneous reading from the load cell.
63    /// - `position_limit`: The absolute position boundary. If reached before the load, an error occurs.
64    /// - `hysteresis`: The acceptable noise band (minimum 1.0) to prevent premature stopping.
65    pub fn call(
66        &mut self,
67        axis: &mut impl AxisHandle,
68        execute: bool,
69        target_load: f64,
70        current_load: f64,
71        position_limit: f64,
72        hysteresis: f64,
73    ) {
74        let hyst = hysteresis.max(1.0);
75
76        if !execute {
77            if self.active {
78                axis.halt();
79            }
80            self.done = false;
81            self.active = false;
82            self.error = false;
83            self.state.index = 0;
84            return;
85        }
86
87        // Safety: Check for axis faults
88        if axis.is_error() && self.state.index != 100 {
89            self.error = true;
90            self.active = false;
91            self.state.set_error(120, "Axis is in error state");
92            self.state.index = 100;
93        }
94
95        match self.state.index {
96            0 => { // Idle -> Start
97                self.done = false;
98                self.error = false;
99                self.active = true;
100                self.state.clear_error();
101
102                self.moving_negative = current_load > target_load;
103
104                // Check if we're already there
105                let reached = if self.moving_negative {
106                    current_load <= target_load + hyst
107                } else {
108                    current_load >= target_load - hyst
109                };
110
111                if reached {
112                    self.done = true;
113                    self.active = false;
114                    self.state.index = 30;
115                } else {
116                    // Are we already out of bounds?
117                    if (self.moving_negative && axis.position() <= position_limit) ||
118                       (!self.moving_negative && axis.position() >= position_limit) {
119                        self.error = true;
120                        self.active = false;
121                        self.state.set_error(110, "Axis already past position limit before starting");
122                        self.state.index = 100;
123                    } else {
124                        let vel = axis.config().jog_speed;
125                        let acc = axis.config().jog_accel;
126                        let dec = axis.config().jog_decel;
127                        axis.move_absolute(position_limit, vel, acc, dec);
128                        self.state.index = 10;
129                    }
130                }
131            }
132            10 => { // Moving
133                let reached = if self.moving_negative {
134                    current_load <= target_load + hyst
135                } else {
136                    current_load >= target_load - hyst
137                };
138
139                if reached {
140                    axis.halt();
141                    self.state.index = 20; // Wait to stop
142                } else {
143                    // Check limit
144                    let hit_limit = if self.moving_negative {
145                        axis.position() <= position_limit + 0.0001
146                    } else {
147                        axis.position() >= position_limit - 0.0001
148                    };
149
150                    if hit_limit || !axis.is_busy() {
151                        axis.halt();
152                        self.error = true;
153                        self.active = false;
154                        self.state.set_error(150, "Reached position limit without hitting target load");
155                        self.state.index = 100;
156                    }
157                }
158            }
159            20 => { // Stopping
160                if !axis.is_busy() {
161                    self.done = true;
162                    self.active = false;
163                    self.state.index = 30;
164                }
165            }
166            30 => { // Done
167                // Wait for execute to drop
168            }
169            100 => { // Error
170                // Wait for execute to drop
171            }
172            _ => {
173                self.state.index = 0;
174            }
175        }
176        
177        self.state.call();
178    }
179}