mirror of
https://github.com/synthetos/g2.git
synced 2026-09-20 17:55:43 +08:00
Separated canonical machine feedhold functions and alarm handlers into separate files
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* alarm.cpp - canonical machine alarm handlers
|
||||
* This file is part of the g2core project
|
||||
*
|
||||
* Copyright (c) 2010 - 2017 Alden S Hart, Jr.
|
||||
* Copyright (c) 2014 - 2017 Robert Giseburt
|
||||
*
|
||||
* This file ("the software") is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2 as published by the
|
||||
* Free Software Foundation. You should have received a copy of the GNU General Public
|
||||
* License, version 2 along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, you may use this file as part of a software library without
|
||||
* restriction. Specifically, if other files instantiate templates or use macros or
|
||||
* inline functions from this file, or you compile this file and link it with other
|
||||
* files to produce an executable, this file does not by itself cause the resulting
|
||||
* executable to be covered by the GNU General Public License. This exception does not
|
||||
* however invalidate any other reasons why the executable file might be covered by the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY
|
||||
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "g2core.h" // #1
|
||||
#include "config.h" // #2
|
||||
#include "gcode.h" // #3
|
||||
#include "canonical_machine.h"
|
||||
#include "planner.h"
|
||||
#include "report.h"
|
||||
#include "spindle.h"
|
||||
#include "coolant.h"
|
||||
#include "temperature.h"
|
||||
#include "util.h"
|
||||
|
||||
/********************************************************************************
|
||||
* ALARM, SHUTDOWN, and PANIC are nested dolls.
|
||||
*
|
||||
* cm_alrm() - invoke alarm from command
|
||||
* cm_shutd() - invoke shutdown from command
|
||||
* cm_pnic() - invoke panic from command
|
||||
* cm_clr() - clear alarm or shutdown from command
|
||||
*
|
||||
* The alarm states can be invoked from the above commands for testing and clearing
|
||||
*/
|
||||
stat_t cm_alrm(nvObj_t *nv) // invoke alarm from command
|
||||
{
|
||||
cm_alarm(STAT_ALARM, "sent by host");
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
stat_t cm_shutd(nvObj_t *nv) // invoke shutdown from command
|
||||
{
|
||||
cm_shutdown(STAT_SHUTDOWN, "sent by host");
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
stat_t cm_pnic(nvObj_t *nv) // invoke panic from command
|
||||
{
|
||||
cm_panic(STAT_PANIC, "sent by host");
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
stat_t cm_clr(nvObj_t *nv) // clear alarm or shutdown from command line
|
||||
{
|
||||
cm_clear();
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_clear() - clear ALARM and SHUTDOWN states
|
||||
* cm_parse_clear() - parse incoming gcode for M30 or M2 clears if in ALARM state
|
||||
*
|
||||
* Parse clear interprets an M30 or M2 PROGRAM_END as a $clear condition and clear ALARM
|
||||
* but not SHUTDOWN or PANIC. Assumes Gcode string has no leading or embedded whitespace
|
||||
*/
|
||||
|
||||
void cm_clear()
|
||||
{
|
||||
if (cm->machine_state == MACHINE_ALARM) {
|
||||
cm->machine_state = MACHINE_PROGRAM_STOP;
|
||||
} else if (cm->machine_state == MACHINE_SHUTDOWN) {
|
||||
cm->machine_state = MACHINE_READY;
|
||||
}
|
||||
}
|
||||
|
||||
void cm_parse_clear(const char *s)
|
||||
{
|
||||
if (cm->machine_state == MACHINE_ALARM) {
|
||||
if (toupper(s[0]) == 'M') {
|
||||
if (( (s[1]=='3') && (s[2]=='0') && (s[3]==0)) || ((s[1]=='2') && (s[2]==0) )) {
|
||||
cm_clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_is_alarmed() - return alarm status code or OK if no alarms
|
||||
*/
|
||||
|
||||
stat_t cm_is_alarmed()
|
||||
{
|
||||
if (cm->machine_state == MACHINE_ALARM) { return (STAT_COMMAND_REJECTED_BY_ALARM); }
|
||||
if (cm->machine_state == MACHINE_SHUTDOWN) { return (STAT_COMMAND_REJECTED_BY_SHUTDOWN); }
|
||||
if (cm->machine_state == MACHINE_PANIC) { return (STAT_COMMAND_REJECTED_BY_PANIC); }
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_halt_all() - stop, spindle and coolant immediately
|
||||
* cm_halt_motion() - stop motion immediately. Does not affect spindle, coolant, or other IO
|
||||
*
|
||||
* Stop motors and reset all system states accordingly.
|
||||
* Does not de-energize motors as in some cases the motors must remain energized
|
||||
* in order to prevent an axis from crashing.
|
||||
*/
|
||||
|
||||
void cm_halt_all(void)
|
||||
{
|
||||
cm_halt_motion();
|
||||
cm_spindle_off_immediate();
|
||||
cm_coolant_off_immediate();
|
||||
}
|
||||
|
||||
void cm_halt_motion(void)
|
||||
{
|
||||
mp_halt_runtime(); // stop the runtime. Do this immediately. (Reset is in cm_clear)
|
||||
canonical_machine_reset(cm); // halt the currently active machine
|
||||
cm->cycle_state = CYCLE_OFF; // Note: leaves machine_state alone
|
||||
cm->motion_state = MOTION_STOP;
|
||||
cm->hold_state = FEEDHOLD_OFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_alarm() - enter ALARM state
|
||||
*
|
||||
* An ALARM sets the ALARM machine state, starts a feedhold to stop motion, stops the
|
||||
* spindle, turns off coolant, clears out queued planner moves and serial input,
|
||||
* and rejects new action commands (gcode blocks, SET commands, and other actions)
|
||||
* until the alarm is cleared.
|
||||
*
|
||||
* ALARM is typically entered by a soft limit or a limit switch being hit. In the
|
||||
* limit switch case the INPUT_ACTION will override the feedhold - i.e. if the
|
||||
* input action is "FAST_STOP" or "HALT" that setting will take precedence over
|
||||
* the feedhold native to the alarm function.
|
||||
*
|
||||
* Gcode and machine state is preserved. It may be possible to recover the job from
|
||||
* an alarm, but in many cases this is not possible. Since ALARM attempts to preserve
|
||||
* Gcode and machine state it does not END the job.
|
||||
*
|
||||
* ALARM may also be invoked from the command line using {alarm:n} or $alarm
|
||||
* ALARM can be manually cleared by entering: {clear:n}, {clr:n}, $clear, or $clr
|
||||
* ALARMs will also clear on receipt of an M30 or M2 command if one is received
|
||||
* while draining the host command queue.
|
||||
*/
|
||||
|
||||
stat_t cm_alarm(const stat_t status, const char *msg)
|
||||
{
|
||||
if ((cm->machine_state == MACHINE_ALARM) || (cm->machine_state == MACHINE_SHUTDOWN) ||
|
||||
(cm->machine_state == MACHINE_PANIC)) {
|
||||
return (STAT_OK); // don't alarm if already in an alarm state
|
||||
}
|
||||
cm->machine_state = MACHINE_ALARM;
|
||||
cm_request_feedhold(); // stop motion
|
||||
cm_request_queue_flush(); // do a queue flush once runtime is not busy
|
||||
|
||||
// TBD - these functions should probably be called - See cm_shutdown()
|
||||
// cm_spindle_control_immediate(SPINDLE_OFF);
|
||||
// cm_coolant_off_immediate();
|
||||
// cm_spindle_optional_pause(spindle.pause_on_hold);
|
||||
// cm_coolant_optional_pause(coolant.pause_on_hold);
|
||||
rpt_exception(status, msg); // send alarm message
|
||||
|
||||
// If "stat" is in the status report, we need to poke it to send.
|
||||
sr_request_status_report(SR_REQUEST_TIMED);
|
||||
return (status);
|
||||
}
|
||||
/*
|
||||
* cm_shutdown() - enter shutdown state
|
||||
*
|
||||
* SHUTDOWN stops all motion, spindle and coolant immediately, sets a SHUTDOWN machine
|
||||
* state, clears out queued moves and serial input, and rejects new action commands
|
||||
* (gcode blocks, SET commands, and some others).
|
||||
*
|
||||
* Shutdown is typically invoked as an electrical input signal sent to the board as
|
||||
* part of an external emergency stop (Estop). Shutdown is meant to augment but not
|
||||
* replace the external Estop functions that shut down power to motors, spindles and
|
||||
* other moving parts.
|
||||
*
|
||||
* Shutdown may also be invoked from the command line using {shutd:n} or $shutd
|
||||
* Shutdown must be manually cleared by entering: {clear:n}, {clr:n}, $clear, or $clr
|
||||
* Shutdown does not clear on M30 or M2 Gcode commands
|
||||
*/
|
||||
|
||||
stat_t cm_shutdown(const stat_t status, const char *msg)
|
||||
{
|
||||
if ((cm->machine_state == MACHINE_SHUTDOWN) || (cm->machine_state == MACHINE_PANIC)) {
|
||||
return (STAT_OK); // don't shutdown if shutdown or panic'd
|
||||
}
|
||||
cm_halt_motion(); // halt motors (may have already been done from GPIO)
|
||||
spindle_reset(); // stop spindle immediately and set speed to 0 RPM
|
||||
coolant_reset(); // stop coolant immediately
|
||||
temperature_reset(); // turn off heaters and fans
|
||||
cm_queue_flush(); // flush all queues and reset positions
|
||||
|
||||
for (uint8_t i = 0; i < HOMING_AXES; i++) { // unhome axes and the machine
|
||||
cm->homed[i] = false;
|
||||
}
|
||||
cm->homing_state = HOMING_NOT_HOMED;
|
||||
|
||||
cm->machine_state = MACHINE_SHUTDOWN; // do this after all other activity
|
||||
rpt_exception(status, msg); // send exception report
|
||||
return (status);
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_panic() - enter panic state
|
||||
*
|
||||
* PANIC occurs if the firmware has detected an unrecoverable internal error
|
||||
* such as an assertion failure or a code condition that should never occur.
|
||||
* It sets PANIC machine state, and leaves the system inspect able (if possible).
|
||||
*
|
||||
* PANIC can only be exited by a hardware reset or soft reset (^x)
|
||||
*/
|
||||
|
||||
stat_t cm_panic(const stat_t status, const char *msg)
|
||||
{
|
||||
_debug_trap(msg);
|
||||
|
||||
if (cm->machine_state == MACHINE_PANIC) { // only do this once
|
||||
return (STAT_OK);
|
||||
}
|
||||
cm_halt_motion(); // halt motors (may have already been done from GPIO)
|
||||
spindle_reset(); // stop spindle immediately and set speed to 0 RPM
|
||||
coolant_reset(); // stop coolant immediately
|
||||
temperature_reset(); // turn off heaters and fans
|
||||
cm_queue_flush(); // flush all queues and reset positions
|
||||
|
||||
cm->machine_state = MACHINE_PANIC; // don't reset anything. Panics are not recoverable
|
||||
rpt_exception(status, msg); // send panic report
|
||||
return (status);
|
||||
}
|
||||
+65
-613
File diff suppressed because it is too large
Load Diff
+21
-20
@@ -289,9 +289,10 @@ typedef struct cmToolTable { // struct to keep a global tool tabl
|
||||
|
||||
/**** Externs - See canonical_machine.cpp for allocation ****/
|
||||
|
||||
extern cmMachine_t *cm; // pointer to active canonical machine
|
||||
extern cmMachine_t cm1; // canonical machine primary machine
|
||||
extern cmMachine_t cm2; // canonical machine secondary machine
|
||||
extern cmMachineSelect cm_select; // CM_PRIMARY, CM_SECONDARY, CM_SECONDARY_RETURN
|
||||
extern cmMachine_t *cm; // pointer to active canonical machine
|
||||
extern cmMachine_t cm1; // canonical machine primary machine
|
||||
extern cmMachine_t cm2; // canonical machine secondary machine
|
||||
extern cmToolTable_t tt;
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -433,7 +434,20 @@ stat_t cm_mfo_control(const float P_word, const bool P_flag); // M50
|
||||
stat_t cm_mto_control(const float P_word, const bool P_flag); // M50.1
|
||||
// See spindle.cpp for cm_sso_control() // M51
|
||||
|
||||
// Feedhold and related functions
|
||||
// Program Functions (4.3.10)
|
||||
void cm_cycle_start(void); // (no Gcode)
|
||||
void cm_cycle_end(void); // (no Gcode)
|
||||
void cm_canned_cycle_end(void); // end of canned cycle
|
||||
void cm_program_stop(void); // M0
|
||||
void cm_optional_program_stop(void); // M1
|
||||
void cm_program_end(void); // M2
|
||||
|
||||
stat_t cm_json_command(char *json_string); // M100
|
||||
stat_t cm_json_wait(char *json_string); // M102
|
||||
|
||||
/*--- Cycles ---*/
|
||||
|
||||
// Feedhold and related functions (cycle_feedhold.cpp)
|
||||
void cm_request_feedhold(void);
|
||||
void cm_request_end_hold(void);
|
||||
void cm_request_queue_flush(void);
|
||||
@@ -450,32 +464,19 @@ stat_t cm_return_from_hold_callback(void); // main loop cal
|
||||
void cm_queue_flush(void); // flush serial and planner queues with coordinate resets
|
||||
void cm_end_queue_flush(void);
|
||||
|
||||
// Program Functions (4.3.10)
|
||||
void cm_cycle_start(void); // (no Gcode)
|
||||
void cm_cycle_end(void); // (no Gcode)
|
||||
void cm_canned_cycle_end(void); // end of canned cycle
|
||||
void cm_program_stop(void); // M0
|
||||
void cm_optional_program_stop(void); // M1
|
||||
void cm_program_end(void); // M2
|
||||
|
||||
stat_t cm_json_command(char *json_string); // M100
|
||||
stat_t cm_json_wait(char *json_string); // M102
|
||||
|
||||
/*--- Cycles ---*/
|
||||
|
||||
// Homing cycles
|
||||
// Homing cycles (cycle_homing.cpp)
|
||||
stat_t cm_homing_cycle_start(const float axes[], const bool flags[]); // G28.2
|
||||
stat_t cm_homing_cycle_start_no_set(const float axes[], const bool flags[]); // G28.4
|
||||
stat_t cm_homing_cycle_callback(void); // G28.2/.4 main loop callback
|
||||
|
||||
// Probe cycles
|
||||
// Probe cycles (cycle_probing.cpp)
|
||||
stat_t cm_straight_probe(float target[],
|
||||
bool flags[],
|
||||
bool failure_is_fatal,
|
||||
bool moving_toward_switch); // G38.x
|
||||
stat_t cm_probing_cycle_callback(void); // G38.x main loop callback
|
||||
|
||||
// Jogging cycle
|
||||
// Jogging cycle (cycle_jogging.cpp)
|
||||
stat_t cm_jogging_cycle_callback(void); // jogging cycle main loop
|
||||
stat_t cm_jogging_cycle_start(uint8_t axis); // {"jogx":-100.3}
|
||||
float cm_get_jogging_dest(void); // get jogging destination
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* cycle_feedhold.cpp - canonical machine feedhold processing
|
||||
* This file is part of the g2core project
|
||||
*
|
||||
* Copyright (c) 2010 - 2017 Alden S Hart, Jr.
|
||||
* Copyright (c) 2014 - 2017 Robert Giseburt
|
||||
*
|
||||
* This file ("the software") is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2 as published by the
|
||||
* Free Software Foundation. You should have received a copy of the GNU General Public
|
||||
* License, version 2 along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, you may use this file as part of a software library without
|
||||
* restriction. Specifically, if other files instantiate templates or use macros or
|
||||
* inline functions from this file, or you compile this file and link it with other
|
||||
* files to produce an executable, this file does not by itself cause the resulting
|
||||
* executable to be covered by the GNU General Public License. This exception does not
|
||||
* however invalidate any other reasons why the executable file might be covered by the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY
|
||||
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "g2core.h" // #1
|
||||
#include "config.h" // #2
|
||||
#include "gcode.h" // #3
|
||||
#include "canonical_machine.h"
|
||||
#include "planner.h"
|
||||
#include "stepper.h"
|
||||
#include "spindle.h"
|
||||
#include "coolant.h"
|
||||
#include "util.h"
|
||||
|
||||
/***********************************************************************************
|
||||
**** CODE *************************************************************************
|
||||
***********************************************************************************/
|
||||
/*
|
||||
* Feedholds, queue flushes and end_holds are all related. The request functions set flags
|
||||
* or change state to "REQUESTED". The sequencing callback interprets the flags as so:
|
||||
* - A feedhold request received during motion should be honored
|
||||
* - A feedhold request received during a feedhold should be ignored
|
||||
* - A feedhold request received during a motion stop should be ignored
|
||||
*
|
||||
* - A queue flush request should only be honored while in a feedhold
|
||||
* - Said queue flush request received during a feedhold should be deferred until
|
||||
* the feedhold enters a HOLD state (i.e. until deceleration is complete and motors stop).
|
||||
* - A queue flush request received during a motion stop should be honored
|
||||
*
|
||||
* - An end_hold (cycle start) request should only be honored while in a feedhold
|
||||
* - Said end_hold request received during a feedhold should be deferred until the
|
||||
* feedhold enters a HOLD state (i.e. until deceleration is complete).
|
||||
* If a queue flush request is also present the queue flush should be done first
|
||||
*
|
||||
* Below the request level, feedholds work like this:
|
||||
* - The hold is initiated by calling cm_start_hold(). cm->hold_state is set to
|
||||
* FEEDHOLD_SYNC, motion_state is set to MOTION_HOLD, and the spindle is turned off
|
||||
* (if it it on). The remainder of feedhold
|
||||
* processing occurs in plan_exec.c in the mp_exec_aline() function.
|
||||
*
|
||||
* - MOTION_HOLD and FEEDHOLD_SYNC tells mp_exec_aline() to begin feedhold processing
|
||||
* after the current move segment is finished (< 5 ms later). (Cases handled by
|
||||
* feedhold processing are listed in plan_exec.c).
|
||||
*
|
||||
* - FEEDHOLD_SYNC causes the current move in mr to be replanned into a deceleration.
|
||||
* If the distance remaining in the executing move is sufficient for a full deceleration
|
||||
* then motion will stop in the current block. Otherwise the deceleration phase
|
||||
* will extend across as many blocks necessary until one will stop.
|
||||
*
|
||||
* - Once deceleration is complete hold state transitions to FEEDHOLD_FINALIZING and
|
||||
* the distance remaining in the bf last block is replanned up from zero velocity.
|
||||
* The move in the bf block is NOT released (unlike normal operation), as it will
|
||||
* be used again to restart from hold.
|
||||
*
|
||||
* - When cm_end_hold() is called it releases the hold, restarts the move and restarts
|
||||
* the spindle if the spindle is active.
|
||||
*/
|
||||
|
||||
/* With the addition of the secondary CM, feedhold state management gets tricky.
|
||||
What you see below is a temporary solution until we decide the general solution.
|
||||
|
||||
The general solution causes a feedhold from the primary context to switch
|
||||
into the secondary context to perform feedhold actions. When in the secondary
|
||||
context an additional feedhold will perform the usual STOP operation, but
|
||||
will remain in the secondary context, and therefore not perform any feedhold
|
||||
actions (lifts, spindle, etc.). This is needed to support homing and probing
|
||||
operations from within the secondary context.
|
||||
|
||||
Oddities of the general solution:
|
||||
- Should we allow a feedhold to be performed if the tool is not moving?
|
||||
Right now we don't, but with the secondary context this might be useful
|
||||
|
||||
What you see here is a Q&D to only allow feedholds from the primary context.
|
||||
It has the following limitations:
|
||||
- Feedhold requests are only honored form the primary context
|
||||
- Queue flush requests are only honored form the primary context
|
||||
- Machine alarm state is not (yet) taken into account in feedhold sequencing and restart
|
||||
*/
|
||||
|
||||
/*
|
||||
* cm_request_feedhold()
|
||||
* cm_request_end_hold()
|
||||
* cm_request_queue_flush()
|
||||
* cm_feedhold_sequencing_callback() - sequence feedhold, queue_flush, and end_hold requests
|
||||
*/
|
||||
|
||||
void cm_request_feedhold(void)
|
||||
{
|
||||
// do not generate a feedhold request from the secondary context
|
||||
if (cm_select != CM_PRIMARY) {
|
||||
return;
|
||||
}
|
||||
// only generate request if not already in a feedhold and the machine is in motion
|
||||
if ((cm1.hold_state == FEEDHOLD_OFF) && (cm1.motion_state != MOTION_STOP)) {
|
||||
cm1.hold_state = FEEDHOLD_REQUESTED;
|
||||
}
|
||||
}
|
||||
|
||||
void cm_request_end_hold(void) // This is usually requested form the secondary context
|
||||
{
|
||||
if (cm1.hold_state != FEEDHOLD_OFF) {
|
||||
cm1.end_hold_requested = true;
|
||||
}
|
||||
}
|
||||
|
||||
void cm_request_queue_flush()
|
||||
{
|
||||
// do not generate a queue flush request from the secondary context
|
||||
if (cm_select != CM_PRIMARY) {
|
||||
return;
|
||||
}
|
||||
if ((cm1.hold_state != FEEDHOLD_OFF) && // don't honor request unless you are in a feedhold
|
||||
(cm1.queue_flush_state == FLUSH_OFF)) { // ...and only once
|
||||
cm1.queue_flush_state = FLUSH_REQUESTED; // request planner flush once motion has stopped
|
||||
|
||||
// NOTE: we used to flush the input buffers, but this is handled in xio *prior* to queue flush now
|
||||
}
|
||||
}
|
||||
|
||||
stat_t cm_feedhold_sequencing_callback()
|
||||
{
|
||||
if (cm1.hold_state == FEEDHOLD_REQUESTED) {
|
||||
cm_start_hold(); // feed won't run unless the machine is moving
|
||||
}
|
||||
if (cm1.hold_state == FEEDHOLD_FINALIZING) {
|
||||
cm1.hold_state = FEEDHOLD_HOLD;
|
||||
cm_switch_to_hold_context(); // perform Z lift, spindle & coolant operations
|
||||
}
|
||||
if (cm1.queue_flush_state == FLUSH_REQUESTED) {
|
||||
cm_queue_flush(); // queue flush won't run until runtime is idle
|
||||
}
|
||||
if (cm1.end_hold_requested) {
|
||||
if (cm1.queue_flush_state == FLUSH_OFF) { // either no flush or wait until it's done flushing
|
||||
cm_end_hold();
|
||||
}
|
||||
}
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_has_hold() - return true if a hold condition exists (or a pending hold request)
|
||||
* cm_start_hold() - start a feedhhold by signalling the exec
|
||||
* cm_end_hold() - end a feedhold by returning the system to normal operation
|
||||
*/
|
||||
bool cm_has_hold()
|
||||
{
|
||||
return (cm1.hold_state != FEEDHOLD_OFF);
|
||||
}
|
||||
|
||||
void cm_start_hold()
|
||||
{
|
||||
if (mp_has_runnable_buffer(mp)) { //+++++ // meaning there's something running
|
||||
cm_set_motion_state(MOTION_HOLD);
|
||||
cm->hold_state = FEEDHOLD_SYNC; // invokes hold from aline execution
|
||||
}
|
||||
}
|
||||
|
||||
void cm_end_hold()
|
||||
{
|
||||
if (cm1.hold_state == FEEDHOLD_HOLD) {
|
||||
cm1.end_hold_requested = false;
|
||||
cm_return_from_hold_context();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_switch_to_hold_context() - switch to secondary machine context
|
||||
*
|
||||
* Moving between contexts is only safe when the machine is completely stopped
|
||||
* either during a feedhold or when idle.
|
||||
*/
|
||||
|
||||
stat_t cm_switch_to_hold_context()
|
||||
{
|
||||
// Must be in the primary CM and fully stopped in a hold
|
||||
if ((cm != &cm1) || (cm->hold_state != FEEDHOLD_HOLD)) {
|
||||
return (STAT_COMMAND_NOT_ACCEPTED);
|
||||
}
|
||||
|
||||
// copy the primary canonical machine to the secondary,
|
||||
// fix the planner pointer, and reset the secondary planner
|
||||
memcpy(&cm2, &cm1, sizeof(cmMachine_t));
|
||||
cm2.mp = &mp2;
|
||||
planner_reset((mpPlanner_t *)cm2.mp); // mp is a void pointer
|
||||
|
||||
// set parameters in cm, gm and gmx so you can actually use it
|
||||
cmMachine_t *_cm = &cm2;
|
||||
_cm->hold_state = FEEDHOLD_OFF;
|
||||
_cm->gm.motion_mode = MOTION_MODE_CANCEL_MOTION_MODE;
|
||||
_cm->gm.absolute_override = ABSOLUTE_OVERRIDE_OFF;
|
||||
_cm->gm.feed_rate = 0;
|
||||
|
||||
// clear the target and set the positions to the current hold position
|
||||
memset(&(_cm->gm.target), 0, sizeof(_cm->gm.target));
|
||||
copy_vector(_cm->gm.target_comp, cm->gm.target_comp); // preserve original Kahan compensation
|
||||
copy_vector(_cm->gmx.position, mr->position);
|
||||
copy_vector(mp2.position, mr->position);
|
||||
copy_vector(mr2.position, mr->position);
|
||||
|
||||
// reassign the globals to the secondary CM
|
||||
cm = &cm2;
|
||||
mp = (mpPlanner_t *)cm->mp; // mp is a void pointer
|
||||
mr = mp->mr;
|
||||
cm_select = CM_SECONDARY;
|
||||
|
||||
// set motion state and ACTIVE_MODEL. This must be performed after cm is set to cm2
|
||||
cm_set_g30_position();
|
||||
cm_set_motion_state(MOTION_STOP);
|
||||
|
||||
// optional Z lift
|
||||
if (fp_NOT_ZERO(cm->feedhold_z_lift)) {
|
||||
float stored_distance_mode = cm_get_distance_mode(MODEL);
|
||||
cm_set_distance_mode(INCREMENTAL_DISTANCE_MODE);
|
||||
bool flags[] = { 0,0,1,0,0,0 };
|
||||
float target[] = { 0,0, cm->feedhold_z_lift, 0,0,0 };
|
||||
cm_straight_traverse(target, flags);
|
||||
cm_set_distance_mode(stored_distance_mode);
|
||||
}
|
||||
|
||||
// optional spindle stop
|
||||
if (spindle.pause_on_hold) {
|
||||
|
||||
}
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* cm_return_from_hold_context() - initiate return from secondary context
|
||||
* cm_return_from_hold_callback() - main loop callback to finsh return once moves are done
|
||||
* _planner_done_callback() - callback to sync to end of planner operations
|
||||
*
|
||||
* Moving between contexts is only safe when the machine is completely stopped
|
||||
* either during a feedhold or when idle.
|
||||
*/
|
||||
|
||||
// Callback to run at when the G30 return move is finished
|
||||
static void _planner_done_callback(float* vect, bool* flag)
|
||||
{
|
||||
cm2.waiting_for_planner_done = false;
|
||||
}
|
||||
|
||||
stat_t cm_return_from_hold_context() // LATER: if value == true return with offset corrections
|
||||
{
|
||||
// Must be in the secondary CM and fully stopped
|
||||
if ((cm != &cm2) || (cm->motion_state != MOTION_STOP)) {
|
||||
return (STAT_COMMAND_NOT_ACCEPTED);
|
||||
}
|
||||
|
||||
// *** While still in secondary machine:
|
||||
/*
|
||||
if (cm->machine_state == MACHINE_ALARM) {
|
||||
cm_spindle_off_immediate();
|
||||
cm_coolant_off_immediate();
|
||||
*/
|
||||
// restart spindle (with optional dwell)
|
||||
|
||||
// restart coolant
|
||||
|
||||
// perform the G30 move and queue a wait
|
||||
float target[] = { 0,0,0,0,0,0 }; // LATER: Make this move return through XY, then Z
|
||||
bool flags[] = { 0,0,0,0,0,0 };
|
||||
cm_goto_g30_position(target, flags); // initiate a return move
|
||||
cm->waiting_for_planner_done = true; // indicates running the final G30 move in the secondary
|
||||
mp_queue_command(_planner_done_callback, nullptr, nullptr);
|
||||
cm_select = CM_SECONDARY_RETURN;
|
||||
return (STAT_OK);
|
||||
|
||||
// return_to_primary completes in cm_return_callback() after the wait
|
||||
}
|
||||
|
||||
stat_t cm_return_from_hold_callback()
|
||||
{
|
||||
if (cm_select != CM_SECONDARY_RETURN) { // exit if not in secondary planner
|
||||
return (STAT_NOOP);
|
||||
}
|
||||
if (cm->waiting_for_planner_done) { // sync to planner move ends (via _return_move_callback)
|
||||
return (STAT_EAGAIN);
|
||||
}
|
||||
|
||||
// return to primary machine
|
||||
cm = &cm1;
|
||||
mp = (mpPlanner_t *)cm->mp; // cm->mp is a void pointer
|
||||
mr = mp->mr;
|
||||
cm_select = CM_PRIMARY;
|
||||
|
||||
cm->hold_state = FEEDHOLD_OFF;
|
||||
if (mp_has_runnable_buffer(mp)) { //+++++ Should MP be passed or global?
|
||||
cm_set_motion_state(MOTION_RUN);
|
||||
cm_cycle_start();
|
||||
st_request_exec_move();
|
||||
sr_request_status_report(SR_REQUEST_IMMEDIATE);
|
||||
} else {
|
||||
cm_set_motion_state(MOTION_STOP);
|
||||
cm_cycle_end();
|
||||
}
|
||||
return (STAT_OK);
|
||||
}
|
||||
|
||||
/* Queue Flush operation
|
||||
*
|
||||
* This one's complicated. See here first:
|
||||
* https://github.com/synthetos/g2/wiki/Alarm-Processing
|
||||
* https://github.com/synthetos/g2/wiki/Job-Exception-Handling
|
||||
*
|
||||
* We want to use queue flush for a few different use cases, as per the above wiki pages.
|
||||
* The % behavior implements Exception Handling cases 1 and 2 - Stop a Single Move and
|
||||
* Stop Multiple Moves. This is complicated further by the processing in single USB and
|
||||
* dual USB being different. Also, the state handling is located in xio.cpp / readline(),
|
||||
* controller.cpp _dispatch_kernel() and cm_request_queue_flush(), below.
|
||||
* So it's documented here.
|
||||
*
|
||||
* Single or Dual USB Channels:
|
||||
* - If a % is received outside of a feed hold or ALARM state, ignore it.
|
||||
* Change the % to a ; comment symbol (xio)
|
||||
*
|
||||
* Single USB Channel Operation:
|
||||
* - Enter a feedhold (!)
|
||||
* - Receive a queue flush (%) Both dispatch it and store a marker (ACK) in the input
|
||||
* buffer in place of the the % (xio)
|
||||
* - Execute the feedhold to a hold condition (plan_exec)
|
||||
* - Execute the dispatched % to flush queues (canonical_machine)
|
||||
* - Silently reject any commands up to the % in the input queue (controller)
|
||||
* - When ETX is encountered transition to STOP state (controller/canonical_machine)
|
||||
*
|
||||
* Dual USB Channel Operation:
|
||||
* - Same as above except that we expect the % to arrive on the control channel
|
||||
* - The system will read and dump all commands in the data channel until either a
|
||||
* clear is encountered ({clear:n} or $clear), or an ETX is encountered on either
|
||||
* channel, but it really should be on the data channel to ensure all queued commands
|
||||
* are dumped. It is the host's responsibility to both write the clear (or ETX), and
|
||||
* to ensure that it either arrives on the data channel or that the data channel is
|
||||
* empty before writing it to the control channel.
|
||||
*/
|
||||
/*
|
||||
* cm_queue_flush() - Flush planner queue and correct model positions
|
||||
*/
|
||||
|
||||
void cm_queue_flush()
|
||||
{
|
||||
if (mp_runtime_is_idle()) { // can't flush planner during movement
|
||||
mp_flush_planner(mp); // +++++ Active planner. Potential cleanup
|
||||
|
||||
for (uint8_t axis = AXIS_X; axis < AXES; axis++) { // set all positions
|
||||
cm_set_position(axis, mp_get_runtime_absolute_position(axis));
|
||||
}
|
||||
if(cm->hold_state == FEEDHOLD_HOLD) { // end feedhold if we're in one
|
||||
cm_end_hold();
|
||||
}
|
||||
cm->queue_flush_state = FLUSH_OFF;
|
||||
qr_request_queue_report(0); // request a queue report, since we've changed the number of buffers available
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,6 +1521,9 @@
|
||||
<OutputPath>bin\TestQuadratic\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="alarm.cpp">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="board\Archim\Archim-pinout.h">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
@@ -1692,6 +1695,9 @@
|
||||
<Compile Include="board\sbv300\sbv300-pinout.h">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="cycle_feedhold.cpp">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
<Compile Include="device\neopixel\neopixel.h">
|
||||
<SubType>compile</SubType>
|
||||
</Compile>
|
||||
|
||||
Reference in New Issue
Block a user