static struct {
+ bool holding;
bool writing;
SCurve scurves[AXES];
command_reset_position();
exec_set_velocity(0);
exec_set_cb(0);
- state_idle();
+ if (jr.holding) state_holding();
+ else state_idle();
return STAT_NOP; // Done, no move executed
}
stat_t command_jog(char *cmd) {
- // Ignore jog commands when not READY and not JOGGING
- if (state_get() != STATE_READY && state_get() != STATE_JOGGING)
+ // Ignore jog commands when not READY, HOLDING or JOGGING
+ if (state_get() != STATE_READY && state_get() != STATE_HOLDING &&
+ state_get() != STATE_JOGGING)
return STAT_NOP;
// Skip over command code
if (state_get() != STATE_JOGGING) {
memset(&jr, 0, sizeof(jr));
+ jr.holding = state_get() == STATE_HOLDING;
+
for (int axis = 0; axis < AXES; axis++)
if (axis_is_enabled(axis))
jr.scurves[axis] =
}
-static void _set_hold_reason(hold_reason_t reason) {
- if (s.hold_reason == reason) return; // No change
- s.hold_reason = reason;
-}
-
-
+static void _set_hold_reason(hold_reason_t reason) {s.hold_reason = reason;}
bool state_is_flushing() {return s.flushing && !s.resuming;}
bool state_is_resuming() {return s.resuming;}
-bool state_is_quiescent() {
+static bool _is_idle() {
return (state_get() == STATE_READY || state_get() == STATE_HOLDING) &&
!st_is_busy();
}
void state_jogging() {
- if (state_get() == STATE_READY) _set_state(STATE_JOGGING);
+ if (state_get() == STATE_READY || state_get() == STATE_HOLDING)
+ _set_state(STATE_JOGGING);
}
s.stop_requested = false;
}
- // Only flush queue when idle or holding
- if (s.flushing && state_is_quiescent()) {
+ // Only flush queue when idle (READY or HOLDING)
+ if (s.flushing && _is_idle()) {
command_flush_queue();
// Resume
- if (s.resuming) {
- s.flushing = s.resuming = false;
- _set_state(STATE_READY);
- }
+ if (s.resuming) s.flushing = s.resuming = false;
}
// Unpause
self.ctrl = ctrl
self.planner = bbctrl.Planner(ctrl)
self.unpausing = False
+ self.last_cycle = 'idle'
ctrl.state.set('cycle', 'idle')
self._update_cycle_cb(False)
def _get_state(self): return self.ctrl.state.get('xx', '')
+ def _is_holding(self): return self._get_state() == 'HOLDING'
+ def _get_pause_reason(self): return self.ctrl.state.get('pr', '')
def _get_cycle(self): return self.ctrl.state.get('cycle')
+ def _can_jog(self):
+ return (self._get_cycle() == 'idle' or
+ (self._is_holding() and
+ self._get_pause_reason() in ('User pause', 'Program pause')))
+
+
def _begin_cycle(self, cycle):
current = self._get_cycle()
if current == cycle: return # No change
- if current == 'idle':
+ if (current == 'idle' or (cycle == 'jogging' and self._can_jog())):
self.planner.update_position()
self.ctrl.state.set('cycle', cycle)
+ self.last_cycle = current
else:
raise Exception('Cannot enter %s cycle while in %s cycle' %
# Handle EStop
if update.get('xx', '') == 'ESTOPPED': self.planner.reset()
- # Unpause sync
- if update.get('xx', '') != 'HOLDING': self.unpausing = False
-
- # Update cycle now, if it has changed
- self._update_cycle()
-
# Detect motor faults
for motor in range(4):
key = '%ddf' % motor
if key in update and update[key] & 0x1f:
log.error(motor_fault_error % motor)
- # Unpause
- if (('xx' in update or 'pr' in update) and
- self.ctrl.state.get('xx', '') == 'HOLDING'):
- pause_reason = self.ctrl.state.get('pr', '')
+ # Update cycle now, if it has changed
+ self._update_cycle()
- # Continue after seek hold
- if (pause_reason == 'Switch found' and
- self.planner.is_synchronizing()):
- self._unpause()
+ # Unpause sync
+ if update.get('xx', 'HOLDING') != 'HOLDING': self.unpausing = False
- # Continue after stop hold
- if pause_reason == 'User stop':
- self.planner.stop()
- self.planner.update_position()
- self.ctrl.state.set('line', 0)
- self._unpause()
+ # Entering HOLDING state
+ if update.get('xx', '') == 'HOLDING':
+ # Always flush queue after pause
+ super().i2c_command(Cmd.FLUSH)
+ super().resume()
+ # Return from jogging cycle
+ if self._get_cycle() == 'jogging':
+ self.ctrl.state.set('cycle', self.last_cycle)
- def _unpause(self):
- if self._get_state() != 'HOLDING' or self.unpausing: return
- self.unpausing = True
+ # Automatically unpause after seek or stop hold
+ if (('xx' in update or 'pr' in update) and self._is_holding() and
+ self._get_pause_reason() in ('Switch found', 'User stop')):
+ self._unpause()
- pause_reason = self.ctrl.state.get('pr', '')
- log.info('Unpause: ' + pause_reason)
- if pause_reason in ['User pause', 'Switch found']:
- self.planner.restart()
+ def _unpause(self):
+ pause_reason = self._get_pause_reason()
+ log.info('Unpause: ' + pause_reason)
- if pause_reason in ['User pause', 'User stop', 'Switch found']:
- super().i2c_command(Cmd.FLUSH)
- super().resume()
+ if pause_reason == 'User stop':
+ self.planner.stop()
+ self.ctrl.state.set('line', 0)
+ else: self.planner.restart()
super().i2c_command(Cmd.UNPAUSE)
@overrides(Comm)
def comm_next(self):
- if self.planner.is_running(): return self.planner.next()
+ if self.planner.is_running() and not self._is_holding():
+ return self.planner.next()
@overrides(Comm)
def unpause(self):
- if self._get_state() != 'HOLDING': return
+ if self._get_state() != 'HOLDING' or self.unpausing: return
- pause_reason = self.ctrl.state.get('pr', '')
- if pause_reason in ['User pause', 'Program pause']:
+ if self._get_pause_reason() in ('User pause', 'Program pause'):
+ self.unpausing = True
self._unpause()