Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions docs/src/gcode/g-code.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ as the 'L number', and so on for any other letter.
|<<gcode:g43,G43>> |Use Tool Length Offset from Tool Table
|<<gcode:g43.1,G43.1>> |Dynamic Tool Length Offset
|<<gcode:g43.2,G43.2>> |Apply additional Tool Length Offset
|<<gcode:g43.4,G43.4>> |Tool Length Offset on Primary Kinematics
|<<gcode:g49,G49>> |Cancel Tool Length Offset
|<<gcode:g52,G52>> |Local Coordinate System Offset
|<<gcode:g53,G53>> |Move in Machine Coordinates
Expand Down Expand Up @@ -1717,11 +1718,59 @@ It is an error if:

NOTE: G43.2 does not write to the tool table.

[[gcode:g43.4]]
== G43.4 Tool Length Offset on Primary Kinematics(((G43.4 Tool Length Offset on Primary Kinematics)))

[source,ngc]
----
G43.4 <H->
----

* 'H' - tool number (optional)

'G43.4' is 'G43' together with a switch to the kinematics the module
declares its working transform, so the program runs with tool length
compensation in the module's working kinematics. The switch happens
first and the offset applies after it, as if the two had been written
on consecutive lines. 'G49' is the matching cancel: it clears the
offset and switches back to identity kinematics, as long as the offset
in effect is still 'G43.4''s and no 'G12.1' or 'G13.1' has selected a
kinematics since.

The H word, the offset itself and the parameters it lands in are
'G43''s. Which kinematics is the working one is declared by the module
(KINSTYPE_PRIMARY, see the <<cha:switchable-kinematics,Switchable
Kinematics>> chapter), the number is not the answer; a module that
declares none rejects 'G43.4' at read time.

The switch is a queue synchronisation point like
'<<gcode:g12.1-g13.1,G12.1>>': when 'G43.4' or 'G49' changes the
kinematics, the interpreter waits for queued motion to finish first, so
both stop any blending in progress. Already on the target kinematics
they do not switch and blend normally. A plain 'G43', 'G43.1' or
'G43.2' never switches, whatever kinematics is selected, and the 'G49'
that cancels one of them does not switch either.

On a machine without switchable kinematics there is nothing to switch:
'G43.4' is a plain 'G43' and 'G49' a plain cancel.

It is an error if:

* the kinematics module is switchable but declares no primary
kinematics, or
* any of the 'G43' error conditions holds.

[[gcode:g49]]
== G49 Cancel Tool Length Compensation(((G49 Cancel Tool Length Offset)))

* 'G49' - cancels tool length compensation

'G49' also switches a switchable kinematics module back to its identity
kinematics when it cancels a 'G43.4', undoing the switch that made. It
leaves a kinematics selected by 'G12.1' or 'G13.1' alone, as it does
after a plain 'G43', and a module that declares no identity kinematics
gets the plain cancel.

It is OK to program using the same offset already in use. It is also
OK to program using no tool length offset if none is currently being
used.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/gcode/overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,7 @@ The modal groups are shown in the following Table.
|Feed Rate Mode (Group 5) | G93, G94, G95
|Units (Group 6) | G20, G21
|Cutter Diameter Compensation (Group 7) | G40, G41, G42, G41.1, G42.1
|Tool Length Offset (Group 8) | G43, G43.1, G49
|Tool Length Offset (Group 8) | G43, G43.1, G43.2, G43.4, G49
|Canned Cycles Return Mode (Group 10) | G98, G99
|Coordinate System (Group 12) | G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3
|Control Mode (Group 13) | G61, G61.1, G64
Expand Down
39 changes: 24 additions & 15 deletions docs/src/motion/switchkins.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,12 @@ A module that declares no identity kinstype refuses 'G13.1' with an
error and can still be driven by number with 'G12.1'; see Code Notes
for how a module declares its types.

See the G-code documentation for 'G12.1' and 'G13.1' for the full
description.
For tool length work there are spellings that name the kinematics by
what it is rather than by number: 'G43.4' applies the tool length
offset and switches to the kinstype the module declares its working
transform, and the 'G49' that cancels it switches back to identity.
See the G-code documentation for 'G43.4' and 'G49', and for 'G12.1'
and 'G13.1', for the full description.

=== M-code commands

Expand Down Expand Up @@ -491,26 +495,31 @@ which kinstype is at fault.
Each kinstype gets its own 'kinstype.is-N' pin, so a module
providing the usual three keeps the pin names it always had.

A module should also declare what each kinstype IS, again from within
A module also declares what each kinstype IS, with flags from
kinematics.h:

. *KINSTYPE_IDENTITY* no transform: the joints are the world
. *KINSTYPE_PRIMARY* the module's working transform

A kinstype registered with switchkinsRegisterOps() carries its flag in
the ops table itself, as the 'identity' or 'primary' field; a kinstype
registered the older way gets it from a call, again from within
switchkinsSetup():

----
int switchkinsDeclare(int ktype, int flags);
----

with flags from kinematics.h:

. *KINSTYPE_IDENTITY* no transform: the joints are the world
. *KINSTYPE_PRIMARY* the module's working transform

G-code reads these declarations: 'G13.1' cancels to the kinstype
declared KINSTYPE_IDENTITY, whatever its number, so a module whose
identity kinematics is not kinstype 0 still gets a working 'G13.1'.
At most one kinstype may be declared identity, and declaring a
kinstype the module does not provide fails the module load. A module
that declares nothing keeps working exactly as before for 'G12.1 P-',
but 'G13.1' is an error, since the number of the identity kinematics
is then a guess.
declared KINSTYPE_IDENTITY, and 'G43.4' switches to the kinstype
declared KINSTYPE_PRIMARY, whatever their numbers, so a module whose
kinematics are not in the conventional order still gets working
spellings. At most one kinstype may be declared identity and at most
one primary, and declaring a kinstype the module does not provide
fails the module load. A module that declares nothing keeps working
exactly as before for 'G12.1 P-' and 'G49', but 'G13.1' and 'G43.4'
are an error, since the numbers of the identity and primary kinematics
are then a guess.

After calling switchkinsSetup(), rtapi_app_main() checks the
supplied parameters, creates a HAL component, and then invokes
Expand Down
10 changes: 9 additions & 1 deletion src/emc/kinematics/kinematics.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include "emcpos.h" /* EmcPose */
#include "rtapi_bool.h"

#ifdef __cplusplus
extern "C" {
#endif

/*
The type of kinematics used.

Expand Down Expand Up @@ -389,7 +393,6 @@ extern int userkKinematicsInverse(const struct EmcPose * world,
double *joint,
const KINEMATICS_INVERSE_FLAGS * iflags,
KINEMATICS_FORWARD_FLAGS * fflags);
#endif
//*********************************************************************
// xyzac,xyzbc;
extern int trtKinematicsSetup(const int comp_id,
Expand Down Expand Up @@ -434,3 +437,8 @@ extern int xyzbcKinematicsWorkFrame(const double *joints,
const KINEMATICS_FORWARD_FLAGS *fflags);

//*********************************************************************
#ifdef __cplusplus
}
#endif

#endif // __LINUXCNC_KINEMATICS_H
2 changes: 1 addition & 1 deletion src/emc/rs274ngc/interp_array.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const int Interp::gees[] = {
/* 360 */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
/* 380 */ -1,-1, 1, 1, 1, 1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
/* 400 */ 7,-1,-1,-1,-1,-1,-1,-1,-1,-1, 7, 7,-1,-1,-1,-1,-1,-1,-1,-1,
/* 420 */ 7, 7,-1,-1,-1,-1,-1,-1,-1,-1, 8, 8, 8,-1,-1,-1,-1,-1,-1,-1,
/* 420 */ 7, 7,-1,-1,-1,-1,-1,-1,-1,-1, 8, 8, 8,-1, 8,-1,-1,-1,-1,-1,
/* 440 */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
/* 460 */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
/* 480 */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 8,-1,-1,-1,-1,-1,-1,-1,-1,-1,
Expand Down
4 changes: 4 additions & 0 deletions src/emc/rs274ngc/interp_base.hh
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public:
virtual void print_state_tag(StateTag const &tag) = 0;
virtual void set_loglevel(int level) = 0;
virtual void set_loop_on_main_m99(bool state) = 0;
// true while the startup code runs at task init, when the motion
// queue cannot drain yet: a kinematics switch there queues without
// the drain-and-assert wait, which could never complete
virtual void set_in_startup_code(bool) {};
virtual FILE* get_stdout() { return stdout; };
};

Expand Down
4 changes: 2 additions & 2 deletions src/emc/rs274ngc/interp_check.cc
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,8 @@ int Interp::check_other_codes(block_pointer block) //!< pointer to a block
}

if (block->h_flag) {
CHKS((block->g_modes[GM_TOOL_LENGTH_OFFSET] != G_43 && motion != G_76 && block->g_modes[GM_TOOL_LENGTH_OFFSET] != G_43_2),
_("H word with no G43 or G76 to use it"));
CHKS((block->g_modes[GM_TOOL_LENGTH_OFFSET] != G_43 && motion != G_76 && block->g_modes[GM_TOOL_LENGTH_OFFSET] != G_43_2 && block->g_modes[GM_TOOL_LENGTH_OFFSET] != G_43_4),
_("H word with no G43, G43.4 or G76 to use it"));
}

if (block->i_flag) { /* could still be useless if yz_plane arc */
Expand Down
104 changes: 75 additions & 29 deletions src/emc/rs274ngc/interp_convert.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#include "interp_internal.hh"
#include "interp_queue.hh"
#include "interp_parameter_def.hh"
#include "kinematics.h" // KINSTYPE_IDENTITY, SWITCHKINS_MAX_TYPES
#include <kinematics.h> // KINSTYPE_IDENTITY, SWITCHKINS_MAX_TYPES

#include "units.h"
#define TOOL_INSIDE_ARC(side, turn) (((side)==CUTTER_COMP::LEFT&&(turn)>0)||((side)==CUTTER_COMP::RIGHT&&(turn)<0))
Expand Down Expand Up @@ -4136,12 +4136,13 @@ int Interp::convert_m(block_pointer block, //!< pointer to a block of RS27

if (FEATURE(RETAIN_G43)) {

if ((settings->active_g_codes[9] == G_43) && ONCE(STEP_RETAIN_G43)) {
if (((settings->active_g_codes[9] == G_43) ||
(settings->active_g_codes[9] == G_43_4)) && ONCE(STEP_RETAIN_G43)) {
if(settings->selected_pocket > 0) {
struct block_struct g43;
init_block(&g43);
block->g_modes[gees[G_43]] = G_43;
CHP(convert_tool_length_offset(G_43, &g43, settings));
block->g_modes[gees[settings->active_g_codes[9]]] = settings->active_g_codes[9];
CHP(convert_tool_length_offset(settings->active_g_codes[9], &g43, settings));
} else {
struct block_struct g49;
init_block(&g49);
Expand Down Expand Up @@ -4451,8 +4452,10 @@ int Interp::convert_modal_0(int code, //!< G-code, must be from group 0
// will be queued: ask every time. The exception is an
// ON_ABORT_COMMAND routine, run by one execute() call that cannot
// service INTERP_EXECUTE_FINISH and would drop the rest of the
// routine; the abort has just flushed the queue anyway.
if (!settings->in_abort_command) {
// routine; the abort has just flushed the queue anyway. The startup
// code is the other exception: it runs before the main loop can
// service the wait, and no motion exists yet to protect.
if (!settings->in_abort_command && !settings->in_startup_code) {
settings->kinsSwitch_flag = true;
}
CHP(convert_kins_switch(code, block, settings));
Expand Down Expand Up @@ -6478,6 +6481,45 @@ int Interp::convert_tool_change(setup_pointer settings) //!< pointer to machine

/****************************************************************************/

// the kinematics module declares what each type is (KINSTYPE_* flags);
// where the flags say nothing at all there is no kinematics attached
// (sai, preview) and the codes fall back to type 0, as before
static int kins_type_info_available()
{
int k;

for (k = 0; k < SWITCHKINS_MAX_TYPES; k++) {
if (GET_EXTERNAL_KINS_TYPE_FLAGS(k) >= 0) return 1;
}
return 0;
}

// the type carrying a KINSTYPE_ flag, or -1 when the module declares none;
// -1 for a type is "no information", and it matches every flag, so it must
// be excluded before the bit test
static int flagged_kins_type(int flag)
{
int k, f;

for (k = 0; k < SWITCHKINS_MAX_TYPES; k++) {
f = GET_EXTERNAL_KINS_TYPE_FLAGS(k);
if (f >= 0 && (f & flag)) { return k; }
}
return -1;
}

// a kinematics switch as G12.1/G13.1 do, with the drain wait and its two
// exceptions; already on the type there is nothing to do
static void switch_kins_type(int kins_type, setup_pointer settings)
{
if (settings->kins_type == kins_type) { return; }
if (!settings->in_abort_command && !settings->in_startup_code) {
settings->kinsSwitch_flag = true;
}
SELECT_KINS_TYPE(kins_type);
settings->kins_type = kins_type;
}

/*! convert_tool_length_offset

Returned Value: int
Expand Down Expand Up @@ -6518,9 +6560,21 @@ int Interp::convert_tool_length_offset(int g_code, //!< g_code being execu

CHKS((settings->cutter_comp_side != CUTTER_COMP::OFF),
(_("Cannot change tool offset with cutter radius compensation on")));
if (g_code == G_43_4) {
int primary = flagged_kins_type(KINSTYPE_PRIMARY);
// G43.4 is G43 on the module's working transform: switch first, then
// apply the offset, as if the switch line had run and drained. With
// no kinematics attached there is nothing to switch to.
CHKS(primary < 0 && kins_type_info_available(), NCE_NO_PRIMARY_KINEMATICS_TYPE);
if (primary >= 0) { switch_kins_type(primary, settings); }
settings->kins_by_g43_4 = true;
} else if (g_code != G_49) {
// the offset in effect is no longer G43.4's, so G49 has no switch to undo
settings->kins_by_g43_4 = false;
}
if (g_code == G_49) {
idx = 0;
} else if (g_code == G_43) {
} else if (g_code == G_43 || g_code == G_43_4) {
logDebug("convert_tool_length_offset h_flag=%d h_number=%d toolchange_flag=%d current_pocket=%d\n",
block->h_flag,block->h_number,settings->toolchange_flag,settings->current_pocket);
if(block->h_flag) {
Expand Down Expand Up @@ -6600,7 +6654,7 @@ int Interp::convert_tool_length_offset(int g_code, //!< g_code being execu
if(block->w_flag) tool_offset.w += block->w_number;
}
} else {
ERS("BUG: Code not G43, G43.1, G43.2, or G49");
ERS("BUG: Code not G43, G43.1, G43.2, G43.4, or G49");
}
USE_TOOL_LENGTH_OFFSET(tool_offset);

Expand Down Expand Up @@ -6641,6 +6695,16 @@ int Interp::convert_tool_length_offset(int g_code, //!< g_code being execu
settings->parameters[5088] = PROGRAM_TO_USER_LEN(tool_offset.v);
settings->parameters[5089] = PROGRAM_TO_USER_LEN(tool_offset.w);

if (g_code == G_49 && settings->kins_by_g43_4) {
// G49 undoes what G43.4 did: after the cancel it drops the machine
// to identity kinematics, as if G13.1 had run on the next line. A
// kinematics the program selected itself is left alone, and a
// module that declares no identity type keeps the plain cancel.
int identity = flagged_kins_type(KINSTYPE_IDENTITY);
if (identity >= 0) { switch_kins_type(identity, settings); }
settings->kins_by_g43_4 = false;
}

return INTERP_OK;
}

Expand Down Expand Up @@ -6698,36 +6762,16 @@ so no motion is ever planned across a change of kinematics.

*/

// the kinematics module declares what each type is (KINSTYPE_* flags);
// where the flags say nothing at all there is no kinematics attached
// (sai, preview) and the codes fall back to type 0, as before
static int kins_type_info_available()
{
int k;

for (k = 0; k < SWITCHKINS_MAX_TYPES; k++) {
if (GET_EXTERNAL_KINS_TYPE_FLAGS(k) >= 0) return 1;
}
return 0;
}

int Interp::convert_kins_switch(int code, //!< G_12_1 or G_13_1
block_pointer block, //!< pointer to a block of RS274 instructions
setup_pointer settings) //!< pointer to machine settings
{
int kins_type;

if (code == G_13_1) {
int k;

// G13.1 cancels to identity kinematics; which type that is, the
// module declares, the number is not the answer
for (k = 0, kins_type = -1; k < SWITCHKINS_MAX_TYPES; k++) {
if (GET_EXTERNAL_KINS_TYPE_FLAGS(k) & KINSTYPE_IDENTITY) {
kins_type = k;
break;
}
}
kins_type = flagged_kins_type(KINSTYPE_IDENTITY);
if (kins_type < 0) {
CHKS(kins_type_info_available(), NCE_NO_IDENTITY_KINEMATICS_TYPE);
kins_type = 0; // no kinematics attached: standalone interpreter
Expand All @@ -6744,6 +6788,8 @@ int Interp::convert_kins_switch(int code, //!< G_12_1 or G_13_1

SELECT_KINS_TYPE(kins_type);
settings->kins_type = kins_type;
// the program has taken the kinematics over from G43.4
settings->kins_by_g43_4 = false;
return INTERP_OK;
}

Expand Down
Loading
Loading