G28.2: home the machine from G-code (all joints, or Pn per joint) - #4172
greatEndian wants to merge 5 commits into
Conversation
|
Would it be possible to add a parameter for homing individual joints? Example:
This would be very useful for configurations with joints that switch between rotary and spindle use. It would also circumvent #3556 that complicates the current way of 'rehoming' |
|
Home individual axis- G28.2XC will force home axis X and C ... for non trivial kinematics we need to start discussion how to handle it per joint.. |
|
What happens if we try to run a program in unhomed state ( will that trigger an interpreter error?): Example 1 : How are these commands handled when used inside a gcode program? (I presume it is like a queue buster command that halts the read ahead until all queued command have been executed) : Example 2 : |
|
Example 1 : Example 2 : |
Using axes is probably enough for the majority of use cases and most machine operators would likely not even know about the joint-axis mappings on their particular machines. |
|
I think the cleanest direction is to keep the G-code surface dead simple and put the real effort into execution. For per-joint homing, Sigma's The part that really needs rethinking isn't the syntax, it's how a queued home executes. Homing only runs in free mode, and nothing currently flips the machine into free for a G-code-triggered home, so just relaxing the motion guard means the command can silently stall in teleop. It needs to be sequenced in task: drain motion, switch to free, home, wait for it to actually finish, then restore the prior mode. And if homing fails, the program has to abort rather than carry on unreferenced. A nice consequence is the trivkins question answers itself: home-all works everywhere, but a partial home can only resume coordinated motion on identity kinematics, because non-trivkins won't re-enter teleop until everything's homed. There's also the unhome-then-keep-running hole Sigma raised. The existing force-homing check only fires at program start, so Given all that, I'd split it: land the explicit |
|
"Homing" is 100% a Joint thing, not an axis thing, so I don't think that I support the G28.2 X C style. Using P1 to home a single joint seems reasonable to me. The command can be repeated to home another joint. However, is is already possible to home through the linuxcncrsh and similar interfaces, so I am not entirely sure there is a use-case for this. One for discussion at a developer meeting, I think. |
As I pointed out above, one use case is the rehoming of joints that are switched between rotary and spindle modes. This needs to be done inside a gcode program and currently requires a rather complex hal setup plus a workaround for #3556 (which is a serious bug) |
Implements the direction from PR LinuxCNC#4172's review discussion instead of the axis-letter form originally sketched there: - Adds an optional Pn word to G28.2/G28.3 to home/unhome a single joint by its 0-based joint number (matching [JOINT_n] INI numbering), e.g. G28.2 P1. Bare G28.2/G28.3 (no P) are unchanged (home/unhome all joints). Reuses the joint field EMC_JOINT_HOME/EMC_JOINT_UNHOME already carry, so it needs no NML change and works identically on any kinematics -- exactly the primitive grandixximo's review comment argued for. The axis-letter form (G28.2 X) is deliberately NOT implemented: resolving an axis letter to a joint needs the kinematics coordinate map and isn't trivial even on trivkins (duplicate letters on gantries), and andypugh's review also objected that homing is a joint concept, not an axis one. G28.2/G28.3 needed adding to the P-word whitelist in interp_check.cc (checked against g_modes[GM_MODAL_0], since they are modal-group-0 codes like G10/G4, not motion-group codes). - Fixes the real gap grandixximo's review identified: do_homing() (control.c) only ever advances while motion is in FREE mode, so a home/unhome issued from a running program or MDI while in TELEOP/COORD would previously either be rejected by a motion-side guard or silently never progress. Task now sequences it properly: a new EMC_TASK_EXEC::WAITING_FOR_HOMING state (modeled on the existing WAITING_FOR_SPINDLE_ORIENTED state) saves the current trajectory mode, dips into FREE, waits for the actual per-joint .homing/.homed status to reach the expected end state, then restores the prior mode -- invisibly to the task-level MDI/AUTO/MANUAL state, the same principle multichannel-DESIGN.txt uses for the analogous per-channel-homing problem. If homing/unhoming does not reach the expected end state, the program aborts (execState = ERROR) rather than continuing unreferenced, and the machine is left in FREE for an operator to intervene from rather than snapped back to a mode an unhomed machine may not legally run coordinated motion in. HOME and UNHOME are not symmetric at the motion level: EMCMOT_JOINT_HOME is a genuine state machine (.homing goes true while running), but EMCMOT_JOINT_UNHOME (command.c) is synchronous -- set_unhomed() just clears .homed immediately, .homing is never touched. The sequencing wait branches accordingly: UNHOME checks the target .homed state directly, HOME waits for the full start-then-finish cycle. - Re-applies [TRAJ]NO_FORCE_HOMING at the point a home/unhome command already forces a sync, closing the hole Sigma1912 raised in the PR discussion (G28.3 mid-program followed by a move with no re-home). NO_FORCE_HOMING=0 already refuses to *start* MDI/AUTO on an unhomed machine, but only at program/MDI start, not per line, so this specific gap needed its own check -- at no cost to the motion path, since it only runs at a sync point the command already forces. - Fixes two bugs found in the process that predate this commit and affect the base G28.2/G28.3/GCODE_HOMING feature, not just Pn: * HOME_CYCLE()/UNHOME_AXES()/HOME_CYCLE_IF_UNHOMED() (emccanon.cc) never flushed pending chained motion segments before appending their own command. STRAIGHT_FEED/STRAIGHT_TRAVERSE buffer points for arc-blend lookahead and only reach interp_list on a flush, so a queued move immediately before a G28.2/G28.3/homing-G28 could silently execute AFTER the home instead of before it. Fixed by calling flush_segments() first in all five home/unhome canon functions (the three pre-existing ones too). * emcJointHome()/emcJointUnhome() (taskintf.cc) returned 0 (success) for an out-of-range joint number, so an invalid Pn would silently report success instead of an error. - Adds stub implementations of the two new canon calls to gcodemodule.cc (the Python gcode module bindings), the third canon backend alongside emccanon.cc and saicanon.cc. Validated live in headless sim: the exact rehoming-a-shared-joint use case Sigma1912 described (cold-start home-all, move, mid-program unhome one joint, rehome it, move again) completes cleanly with zero errors; a NO_FORCE_HOMING=0 config confirms an unreferenced move after an unhome is correctly blocked with the intended error message; a plain move-then-home-all program confirms the flush_segments() ordering fix. Interp-level regression (tests/interp/gcode-homing/*, tests/interp/rotation/g28) passes 4/4, including a new joint-pword test case for the Pn parsing. Signed-off-by: chabron94 <chabron94@gmail.com>
|
Big step in the right direction. The free-mode dip and restore, failure-abort-leave-in-FREE, the HOME/UNHOME asymmetry, the flush_segments ordering fix and the out-of-range Pn fix are all solid. Main concern is the HOME completion check. You infer "homing stopped" from the per-joint Non-trivkins teleop restore: success and restore are gated on the target joint, not Two smaller ones. The sequencing now wraps every Last, |
|
@Sigma1912 on |
|
Tried this gcode: with Machine moves to X10, then rehomes all joints and exits: |
|
I think, as far as I understand the code, in the INI file if you set You should be able to actually run a gcode that starts with G28 Edit: |
|
Correction to my last note: on a closer read it isn't the race I flagged. The race would print "G28.2 home did not complete", and none of Sigma's errors say that. His failures are all mode errors, so this is the other finding, the non-trivkins mode-restore gap. The tell is Fix: after a (un)home that leaves the machine not fully homed, don't restore a coordinated mode. Leave it in FREE and abort with a clear error instead of wedging. And Sigma's opening point is the real one for |
Given that G28.2 (Pn) can be called without the need to unhome first what is the intended use case for G28.3 (Pn)? |
|
I guess that's for @greatEndian to answer, I've been thinking about it, unless it's about compatibility with something out of tree that only supports G28.3 in combination with G28.2 I don't see a good argument to keep it either... |
|
@greatEndian please can you have another look at the questions here and illuminate with some answers? |
|
@Sigma1912 @grandixximo @andypugh thanks for the real-hardware run.. @Sigma1912 — that was worth more than the sim work. Both failures you hit are real bugs, and I can name both. Answers in order.
It is not a precursor to G28.2 — you are right that homing does not need it. It is a way to declare "this joint's reference is no longer physically valid". Your own case is the clearest one: a joint that switches between rotary-axis and spindle use. While it runs as a spindle, the homed flag is a lie — the encoder count no longer corresponds to a known machine position, but nothing in the controller knows that. G28.3 Pn marks it unreferenced, and with NO_FORCE_HOMING=0 the machine then refuses further coordinated motion until G28.2 Pn re-references it. The same applies to anything that invalidates a reference without moving the joint through a normal cycle: a decoupled indexer, a released brake or clutch, a re-gripped chuck. So the pair is: G28.3 Pn = "stop trusting this joint", G28.2 Pn = "trust it again". If that argument does not convince you, I have no objection to landing G28.2 Pn alone and dropping G28.3 — it is the weaker half.
@grandixximo's second diagnosis is correct, it is the mode-restore gap and not the completion race. On success the code restores the prior trajectory mode gated on the target joint only: } else if (success) { On your gantry (non-identity kinematics) switch_to_teleop_mode() refuses unless the whole machine is homed, so after g28.3 p0 the restore is rejected by motion — that is your all joints must be homed before going into coordinated mode — task still marks the command DONE, and nothing recovers the mode. Hence the grayed-out controls and the F2. Test 3 is the same bug cascading: the mode state is already broken before g28 runs. Fix: never restore a coordinated mode when the machine is not fully homed. Leave motion in FREE and abort with a clear operator error instead of wedging. I will also verify the mode actually took rather than assuming emcTrajSetMode() succeeded.
Accepted. Inferring "homing stopped" from the per-joint .homing OR is unsafe, and homing.c says so itself at line 546: "The homing status variable turns false before homing_active state turns false. This means that a new homing command on a joint might fail due to the homing state machine being active while all joints already are in the 'not homing' state." That is exactly the window my poll can land in on a multi-HOME_SEQUENCE home-all, producing a spurious "did not complete" abort on a perfectly good home. Single-joint Pn and single-sequence machines are not exposed, which is why the sim tests and Sigma's test 1 passed. Fix as you suggested: use the aggregate get_homing_is_active() (homing.h:65), plumbed through emcmot status into emcStatus, instead of edge-detecting the per-joint OR. It is not currently in NML status, so that is a small status-field addition.
@Sigma1912's opening question settles it. With NO_FORCE_HOMING=0 a program cannot start unhomed, so on a homed machine G28 is just the ordinary return move and the home-first branch is unreachable. The only way to reach it is to unhome mid-program — which is precisely the path that broke on your machine. A feature whose only reachable path is the broken one does not belong in this PR. It is interleaved with the Pn work in 3d238db and touches ~15 files, so this is a rebase rather than a revert, but it is the right split: land explicit G28.2 / G28.3 (with Pn), hold plain-G28 for its own PR with hardware sign-off.
@andypugh — already dropped. The implemented form is Pn only, for the reasons you and @grandixximo both gave: homing is a joint concept, and an axis letter without a number is a syntax break. There is no G28.2 X C in the branch. Happy to have it on a developer-meeting agenda. Next push
@Sigma1912, if you are willing to re-run tests 2 and 3 after that push, that closes the loop on the part sim cannot reach. Best regards |
…can't hold Addresses the first of grandixximo's findings on PR LinuxCNC#4172, and the failure Sigma1912 hit on real hardware (Mesa 7I95T gantry): "g28.3 p0" reported "all joints must be homed before going into coordinated mode", greyed out the GUI's mode controls, and left the machine needing F2 to recover. The G28.2/G28.3 sequencing dips motion into FREE (do_homing() only advances there) and restores the previous trajectory mode when the command finishes. That restore was gated only on the command having succeeded for its *target* joint. But a per-joint G28.2 Pn / G28.3 Pn can succeed for its own joint while leaving the machine as a whole unreferenced, and motion refuses to (re-)enter TELEOP or COORD in that state on non-identity kinematics -- switch_to_teleop_mode() (motion.c) and the EMCMOT_COORD case (command.c) both gate on "kinType != KINEMATICS_IDENTITY && !get_allhomed()". So the restore was silently rejected while task still reported DONE: the program advanced to the next line, motion had no valid frame for it, and the machine sat stranded in FREE. Sigma's third test is the same bug cascading -- the g28.3 p2 breaks the mode state, then the following g28 and g0 fail with "need to be enabled, in coord mode". Mirror motion's own condition before restoring, and fail the command cleanly (staying in FREE, with an operator error) instead of reporting success and stranding the operator. Identity kinematics are unaffected: motion permits the restore there, so the behaviour is unchanged for trivkins. Note this only reaches the buggy path with NO_FORCE_HOMING=1. With the default 0, the pre-existing NO_FORCE_HOMING re-check catches a partial unhome first -- which, together with the existing tests using trivkins, is why neither the sim tests nor Sigma's first test caught it. The kinematics type is read from emcStatus->motion.traj.kinematics_type rather than this file's static emcmotConfig: that copy is filled in once just before the main loop and never refreshed, so it goes stale as soon as switchkins changes kinematics at runtime (G43.4/G43.5). taskintf.cc re-reads the motion config whenever config_num changes and republishes it in status. Tests: adds gcode-homing/nonidentity-restore, which needs both knobs the existing coverage lacks -- corexykins (KINEMATICS_BOTH) and NO_FORCE_HOMING=1. The program is "G28.3 P0 / M64 P0 / M2"; the digital output is the witness, since it needs no coordinated motion and so would still run with the machine stuck in FREE. Verified the test actually catches the regression by temporarily reverting the fix and confirming it fails -- the interpreter never returns to idle, with dout0=1 proving the program had carried on past the G28.3 -- then restored the fix and confirmed it passes. Verified: tests/interp/gcode-homing (6/6), and the full tests/interp + tests/motion-logger suite (89/89, 1 pre-existing skip). One flush-order failure seen in an earlier sweep did not reproduce (89/89 on re-run, 8/8 in isolation including under load); it uses trivkins, where this change is a no-op by construction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ed path Addresses grandixximo's three remaining findings on PR LinuxCNC#4172. 1. Completion test (his main concern) The HOME completion test inferred "homing has stopped" by OR-ing the per-joint .homing flags. On a machine that homes in several HOME_SEQUENCE groups, the sequence machine finishes one group and can spend a cycle or more before the next group raises .homing, so there is a window in which every joint reads .homing == false while the machine is still homing. Task samples far coarser than the servo cycle, lands in that window, and scores a perfectly good home-all as "did not complete". motion/homing.c documents the same lag in its own words -- "The homing status variable turns false before homing_active state turns false" -- and guards against it internally for exactly this reason. Use motion's aggregate get_homing_is_active() instead. It was not published anywhere, so this plumbs it through as emcmot_status_t.homing_active -> EMC_MOTION_STAT::homing_active, mirroring jogging_active field for field. The per-joint OR is kept as a belt-and-braces term, since it can only extend the "still running" window, never shorten it. EMC_STAT is 8064 bytes against the 20480-byte emcStatus NML buffer, so the added field is free. Single-joint Pn and single-sequence machines never hit the gap, which is why neither the sim tests nor Sigma1912's hardware test 1 caught it. 2. Sequencing applied to immediate commands as well as queued ones EMC_TASK_EXEC::WAITING_FOR_HOMING is only ever reached through emcTaskCheckPostconditions(), which task calls only for commands taken off the interp_list. The GUI's Home and Unhome buttons, halui and linuxcncrsh all send immediate commands: they reach emcTaskIssueCommand() but nothing follows up. Applying the FREE-mode dip to them was a regression in two ways: - the dip was never undone, silently stranding the machine in joint mode; and - because the dip runs before the command is issued, an immediate unhome started succeeding from teleop, where motion deliberately refuses it ("must be in joint mode or disabled to unhome", EMCMOT_JOINT_UNHOME in command.c). The sequencing was quietly granting a permission upstream denies. Scope the whole sequencing to the queued path via issuingQueuedCommand, so immediate home/unhome behaves exactly as it did before this branch. 3. Volatile unhome scoring A volatile unhome (joint == -2) clears only the joints configured VOLATILE_HOME and leaves every other joint homed, so "no joint in range is still homed" would score a correct volatile unhome as a failure. Task cannot narrow the check to just the volatile joints: volatile_home lives in motion's private homing state (H[jno].volatile_home) and is not published in joint status -- the volatile_home in emc_nml.hh belongs to EMC_JOINT_SET_HOMING_PARAMS, a command message, not to EMC_JOINT_STAT. Guarded, but note this is defensive rather than a live fix: G28.3 only ever emits Pn >= 0 or -1, and with the sequencing now scoped to the queued path an immediate unhome(-2) does not reach the scoring either. It is here so a future queued command carrying -2 cannot be silently scored as a failure. Tests: adds gcode-homing/immediate-unhome-mode, asserting an immediate unhome from teleop is refused with the mode untouched, and an immediate home leaves the mode untouched. Verified it catches the regression by neutralising the queued-path scoping and confirming it fails ("immediate unhome from teleop went through (homed=[0, 1, 1])"), then restoring it and confirming it passes. Verified: tests/interp/gcode-homing (7/7) and the full tests/interp + tests/motion-logger + tests/abort suite (94/94, 1 pre-existing skip). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Per the review discussion on PR LinuxCNC#4172: land the explicit G28.2 / G28.3 (with Pn) now, and hold the GCODE_HOMING plain-G28 behaviour for its own PR once it has a real-machine sign-off. Sigma1912's opening question settled it. With NO_FORCE_HOMING=0 a program cannot be started on an unhomed machine at all, so on a homed machine a plain G28 is just the ordinary return move and the home-first branch is unreachable. The only way to reach it is to unhome mid-program -- which is precisely the path that broke on his gantry. A feature whose only reachable path is the broken one does not belong in this PR. Removes the flag and everything gated on it: - interpreter: FEATURE_GCODE_HOMING, the [RS274NGC]GCODE_HOMING INI read, and the G28 home-first branch in convert_home() - canon: HOME_CYCLE_IF_UNHOMED() -- the declaration plus the implementation in all three backends (emccanon.cc, saicanon.cc, gcodemodule.cc) - NML: the EMC_HOME_ALL_IF_UNHOMED (-3) sentinel - task: the sentinel resolution in EMC_JOINT_HOME_TYPE (target_joint is now const), and the stale comment in emcTaskCheckPostconditions() - docs: the g-code.adoc NOTE, the GCODE_HOMING cross-reference in the G28.2/G28.3 note, and the ini-config.adoc entry - tests: gcode-homing/homing-on and gcode-homing/homing-off, which existed only to cover the two settings of the flag, plus stale mentions in joint-pword and flush-order G28.2 / G28.3 (with Pn) are untouched -- they were always independent of the flag. `git grep` for GCODE_HOMING, EMC_HOME_ALL_IF_UNHOMED and HOME_CYCLE_IF_UNHOMED now returns nothing. Done as a removal on top rather than by rewriting history: a merge commit sits partway along this branch, so unpicking 86c58f6 in place would mean replaying the series and would churn commits the reviewers have already read. The net diff of the PR is the same either way. The removed work is preserved intact on the g28-gcode-homing branch for its own PR. Verified: full tests/interp + tests/motion-logger + tests/abort suite (92/92, 1 pre-existing skip). 92 rather than 94 because the two flag tests above are the ones removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The only way I find G28.3 having a use case, is when these codes live in some conditionals that depend on external output, and IMO you must have |
Implements the direction from PR LinuxCNC#4172's review discussion instead of the axis-letter form originally sketched there: - Adds an optional Pn word to G28.2/G28.3 to home/unhome a single joint by its 0-based joint number (matching [JOINT_n] INI numbering), e.g. G28.2 P1. Bare G28.2/G28.3 (no P) are unchanged (home/unhome all joints). Reuses the joint field EMC_JOINT_HOME/EMC_JOINT_UNHOME already carry, so it needs no NML change and works identically on any kinematics -- exactly the primitive grandixximo's review comment argued for. The axis-letter form (G28.2 X) is deliberately NOT implemented: resolving an axis letter to a joint needs the kinematics coordinate map and isn't trivial even on trivkins (duplicate letters on gantries), and andypugh's review also objected that homing is a joint concept, not an axis one. G28.2/G28.3 needed adding to the P-word whitelist in interp_check.cc (checked against g_modes[GM_MODAL_0], since they are modal-group-0 codes like G10/G4, not motion-group codes). - Fixes the real gap grandixximo's review identified: do_homing() (control.c) only ever advances while motion is in FREE mode, so a home/unhome issued from a running program or MDI while in TELEOP/COORD would previously either be rejected by a motion-side guard or silently never progress. Task now sequences it properly: a new EMC_TASK_EXEC::WAITING_FOR_HOMING state (modeled on the existing WAITING_FOR_SPINDLE_ORIENTED state) saves the current trajectory mode, dips into FREE, waits for the actual per-joint .homing/.homed status to reach the expected end state, then restores the prior mode -- invisibly to the task-level MDI/AUTO/MANUAL state, the same principle multichannel-DESIGN.txt uses for the analogous per-channel-homing problem. If homing/unhoming does not reach the expected end state, the program aborts (execState = ERROR) rather than continuing unreferenced, and the machine is left in FREE for an operator to intervene from rather than snapped back to a mode an unhomed machine may not legally run coordinated motion in. HOME and UNHOME are not symmetric at the motion level: EMCMOT_JOINT_HOME is a genuine state machine (.homing goes true while running), but EMCMOT_JOINT_UNHOME (command.c) is synchronous -- set_unhomed() just clears .homed immediately, .homing is never touched. The sequencing wait branches accordingly: UNHOME checks the target .homed state directly, HOME waits for the full start-then-finish cycle. - Re-applies [TRAJ]NO_FORCE_HOMING at the point a home/unhome command already forces a sync, closing the hole Sigma1912 raised in the PR discussion (G28.3 mid-program followed by a move with no re-home). NO_FORCE_HOMING=0 already refuses to *start* MDI/AUTO on an unhomed machine, but only at program/MDI start, not per line, so this specific gap needed its own check -- at no cost to the motion path, since it only runs at a sync point the command already forces. - Fixes two bugs found in the process that predate this commit and affect the base G28.2/G28.3/GCODE_HOMING feature, not just Pn: * HOME_CYCLE()/UNHOME_AXES()/HOME_CYCLE_IF_UNHOMED() (emccanon.cc) never flushed pending chained motion segments before appending their own command. STRAIGHT_FEED/STRAIGHT_TRAVERSE buffer points for arc-blend lookahead and only reach interp_list on a flush, so a queued move immediately before a G28.2/G28.3/homing-G28 could silently execute AFTER the home instead of before it. Fixed by calling flush_segments() first in all five home/unhome canon functions (the three pre-existing ones too). * emcJointHome()/emcJointUnhome() (taskintf.cc) returned 0 (success) for an out-of-range joint number, so an invalid Pn would silently report success instead of an error. - Adds stub implementations of the two new canon calls to gcodemodule.cc (the Python gcode module bindings), the third canon backend alongside emccanon.cc and saicanon.cc. Validated live in headless sim: the exact rehoming-a-shared-joint use case Sigma1912 described (cold-start home-all, move, mid-program unhome one joint, rehome it, move again) completes cleanly with zero errors; a NO_FORCE_HOMING=0 config confirms an unreferenced move after an unhome is correctly blocked with the intended error message; a plain move-then-home-all program confirms the flush_segments() ordering fix. Interp-level regression (tests/interp/gcode-homing/*, tests/interp/rotation/g28) passes 4/4, including a new joint-pword test case for the Pn parsing. Signed-off-by: chabron94 <chabron94@gmail.com>
…can't hold Addresses the first of grandixximo's findings on PR LinuxCNC#4172, and the failure Sigma1912 hit on real hardware (Mesa 7I95T gantry): "g28.3 p0" reported "all joints must be homed before going into coordinated mode", greyed out the GUI's mode controls, and left the machine needing F2 to recover. The G28.2/G28.3 sequencing dips motion into FREE (do_homing() only advances there) and restores the previous trajectory mode when the command finishes. That restore was gated only on the command having succeeded for its *target* joint. But a per-joint G28.2 Pn / G28.3 Pn can succeed for its own joint while leaving the machine as a whole unreferenced, and motion refuses to (re-)enter TELEOP or COORD in that state on non-identity kinematics -- switch_to_teleop_mode() (motion.c) and the EMCMOT_COORD case (command.c) both gate on "kinType != KINEMATICS_IDENTITY && !get_allhomed()". So the restore was silently rejected while task still reported DONE: the program advanced to the next line, motion had no valid frame for it, and the machine sat stranded in FREE. Sigma's third test is the same bug cascading -- the g28.3 p2 breaks the mode state, then the following g28 and g0 fail with "need to be enabled, in coord mode". Mirror motion's own condition before restoring, and fail the command cleanly (staying in FREE, with an operator error) instead of reporting success and stranding the operator. Identity kinematics are unaffected: motion permits the restore there, so the behaviour is unchanged for trivkins. Note this only reaches the buggy path with NO_FORCE_HOMING=1. With the default 0, the pre-existing NO_FORCE_HOMING re-check catches a partial unhome first -- which, together with the existing tests using trivkins, is why neither the sim tests nor Sigma's first test caught it. The kinematics type is read from emcStatus->motion.traj.kinematics_type rather than this file's static emcmotConfig: that copy is filled in once just before the main loop and never refreshed, so it goes stale as soon as switchkins changes kinematics at runtime (G43.4/G43.5). taskintf.cc re-reads the motion config whenever config_num changes and republishes it in status. Tests: adds gcode-homing/nonidentity-restore, which needs both knobs the existing coverage lacks -- corexykins (KINEMATICS_BOTH) and NO_FORCE_HOMING=1. The program is "G28.3 P0 / M64 P0 / M2"; the digital output is the witness, since it needs no coordinated motion and so would still run with the machine stuck in FREE. Verified the test actually catches the regression by temporarily reverting the fix and confirming it fails -- the interpreter never returns to idle, with dout0=1 proving the program had carried on past the G28.3 -- then restored the fix and confirmed it passes. Verified: tests/interp/gcode-homing (6/6), and the full tests/interp + tests/motion-logger suite (89/89, 1 pre-existing skip). One flush-order failure seen in an earlier sweep did not reproduce (89/89 on re-run, 8/8 in isolation including under load); it uses trivkins, where this change is a no-op by construction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ed path Addresses grandixximo's three remaining findings on PR LinuxCNC#4172. 1. Completion test (his main concern) The HOME completion test inferred "homing has stopped" by OR-ing the per-joint .homing flags. On a machine that homes in several HOME_SEQUENCE groups, the sequence machine finishes one group and can spend a cycle or more before the next group raises .homing, so there is a window in which every joint reads .homing == false while the machine is still homing. Task samples far coarser than the servo cycle, lands in that window, and scores a perfectly good home-all as "did not complete". motion/homing.c documents the same lag in its own words -- "The homing status variable turns false before homing_active state turns false" -- and guards against it internally for exactly this reason. Use motion's aggregate get_homing_is_active() instead. It was not published anywhere, so this plumbs it through as emcmot_status_t.homing_active -> EMC_MOTION_STAT::homing_active, mirroring jogging_active field for field. The per-joint OR is kept as a belt-and-braces term, since it can only extend the "still running" window, never shorten it. EMC_STAT is 8064 bytes against the 20480-byte emcStatus NML buffer, so the added field is free. Single-joint Pn and single-sequence machines never hit the gap, which is why neither the sim tests nor Sigma1912's hardware test 1 caught it. 2. Sequencing applied to immediate commands as well as queued ones EMC_TASK_EXEC::WAITING_FOR_HOMING is only ever reached through emcTaskCheckPostconditions(), which task calls only for commands taken off the interp_list. The GUI's Home and Unhome buttons, halui and linuxcncrsh all send immediate commands: they reach emcTaskIssueCommand() but nothing follows up. Applying the FREE-mode dip to them was a regression in two ways: - the dip was never undone, silently stranding the machine in joint mode; and - because the dip runs before the command is issued, an immediate unhome started succeeding from teleop, where motion deliberately refuses it ("must be in joint mode or disabled to unhome", EMCMOT_JOINT_UNHOME in command.c). The sequencing was quietly granting a permission upstream denies. Scope the whole sequencing to the queued path via issuingQueuedCommand, so immediate home/unhome behaves exactly as it did before this branch. 3. Volatile unhome scoring A volatile unhome (joint == -2) clears only the joints configured VOLATILE_HOME and leaves every other joint homed, so "no joint in range is still homed" would score a correct volatile unhome as a failure. Task cannot narrow the check to just the volatile joints: volatile_home lives in motion's private homing state (H[jno].volatile_home) and is not published in joint status -- the volatile_home in emc_nml.hh belongs to EMC_JOINT_SET_HOMING_PARAMS, a command message, not to EMC_JOINT_STAT. Guarded, but note this is defensive rather than a live fix: G28.3 only ever emits Pn >= 0 or -1, and with the sequencing now scoped to the queued path an immediate unhome(-2) does not reach the scoring either. It is here so a future queued command carrying -2 cannot be silently scored as a failure. Tests: adds gcode-homing/immediate-unhome-mode, asserting an immediate unhome from teleop is refused with the mode untouched, and an immediate home leaves the mode untouched. Verified it catches the regression by neutralising the queued-path scoping and confirming it fails ("immediate unhome from teleop went through (homed=[0, 1, 1])"), then restoring it and confirming it passes. Verified: tests/interp/gcode-homing (7/7) and the full tests/interp + tests/motion-logger + tests/abort suite (94/94, 1 pre-existing skip). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
emcJointHome()/emcJointUnhome() bounded the joint number by EMCMOT_MAX_JOINTS, the compile-time size of joints[], instead of by the machine's configured joint count. A number in between the two -- P5 on a five-joint machine -- therefore passed task, reached motion, and was silently dropped: do_home_joint() finds no joint of that number to start and reports nothing at all. Task then waited for a homing cycle that could never begin. With the machine parked in the FREE-mode dip taken for homing sequencing, it sat out the whole two-second start timeout -- the GUI jogging in joint mode meanwhile -- and finally reported the generic "G28.2 home did not start", which names neither the joint nor the real problem. Reported on real hardware (Mesa 7I95T gantry) in PR LinuxCNC#4172 and reproduced exactly in sim: motion_mode read FREE for 2.0s, then COORD again. Bound both entry points by TrajConfig.Joints and report through emcOperatorError() rather than rcs_print(), so an operator who typed "G28.2 P5" sees which numbers the machine actually has. Checked in task rather than in the interpreter because the joint count is not part of interpreter state, and because task covers every caller -- G-code, the GUI Home button, halui, linuxcncrsh -- not just G-code. The unhome path had a check in motion, but as "jno > all_joints", so the first unconfigured joint number fell through it into an unrelated complaint about extra joints; and because an unconfigured joint reads as not homed, task's synchronous "no joint in range is still homed" test would have scored that refusal as a successful unhome. New test tests/interp/gcode-homing/invalid-pword: on a fully homed corexykins machine, G28.2 P3 and G28.3 P7 must each be refused with an error naming the joint, within the start timeout rather than after it, leaving the trajectory mode untouched -- and a valid G28.2 P1 on the same machine must still home. Verified to fail without this fix (2.03s, generic message). Suite 6/6; tests/interp + tests/motion-logger 92/92. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
No reviewer of PR LinuxCNC#4172 could name a use for a G-code unhome that something simpler does not already serve. Sigma1912 asked twice what a program would do with it -- if a joint is no longer trusted, G28.2 re-homes it directly, and the homing cycle fails on its own if anything is wrong -- and grandixximo concluded it only makes sense under [TRAJ]NO_FORCE_HOMING=1, where a numbered parameter carries the same "this joint is unreferenced" flag with no new G-code and no controller state change. It was also the only operation in this PR able to leave a running program on an unreferenced machine. Sigma's "g28.3 p0" on a real gantry unhomed a joint mid-program and left the controller in joint mode with the coordinated modes refused, recoverable only through the GUI. The machinery that made that safe -- the unhome branch of the completion test, the NO_FORCE_HOMING re-check at the sync point, the volatile unhome (-2) guard -- exists solely to contain a hazard the feature itself introduces, so dropping the feature drops all of it. G28.2, bare and with Pn, is unchanged. Unhoming stays available from the GUI, halui and linuxcncrsh, none of which can strand a program mid-run. Removed: G_28_3 and its gees[] slot, the UNHOME_AXES()/UNHOME_JOINT() canon ops and their three implementations, EMC_JOINT_UNHOME_TYPE from the queued-command precondition and postcondition paths, homingIsUnhome and the unhome half of WAITING_FOR_HOMING, and the nonidentity-restore test, which tested the wedge above. EMC_JOINT_UNHOME takes the immediate path only, as it did before this PR. tests/interp 88/88; gcode-homing + motion-logger + motion 13/13. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The out-of-range home error read
Cannot home invalid joint 5 (valid: 0..4, or -1 for all)
which sends the reader straight into a second error, because the
interpreter refuses a negative P word:
P value for G28.2 must be a non-negative whole joint number
-1 (all) and -2 (volatile) are an internal NML convention. They are how
the GUI's Home All button, halui and linuxcncrsh ask for those
operations; G-code cannot express them and is not meant to. Naming them
in an operator-facing message was documenting the API to someone holding
a G-code manual.
Report only what the reader can act on -- the joints this machine has --
and point at the spelling that does what they wanted:
Cannot home invalid joint 5 (this machine has joints 0..4; omit the
joint to home them all)
P value for G28.2 must be a non-negative whole joint number (omit P
to home every joint)
Reported by Sigma1912 on PR LinuxCNC#4172 after re-testing the P5 fix on real
hardware (Mesa 7I95T).
The unhome message loses its sentinels for the same reason, though that
path is reachable only from the interfaces where -1/-2 are valid input.
tests/interp/gcode-homing/invalid-pword now asserts that the rejection
names no negative sentinel, and that G28.2 P-1 is refused with an error
naming the bare form. Both assertions verified to fail against the
previous messages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The directory was named when this branch also carried [RS274NGC]GCODE_HOMING (plain G28 references the machine first) and G28.3 (G-code unhome). Both were dropped from PR LinuxCNC#4172 -- GCODE_HOMING split to its own branch, G28.3 removed outright -- so every test here now exercises G28.2 and nothing else. Rename the directory to tests/interp/g28.2/, and the immediate-unhome-mode subdir to immediate-mode-guard: with G28.3 gone it no longer tests an unhome path specific to G-code, it checks that immediate (GUI/halui/ linuxcncrsh) home and unhome commands keep their pre-PR trajectory-mode behaviour. Pure rename, no content change. Nothing outside the directory referenced the old path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Suq9ikvpe4gvo7PJdjA49y
…model Two gaps grandixximo pointed at in the PR LinuxCNC#4172 review: bare-home-sequence: every existing test drives G28.2 Pn. Bare G28.2 takes a different path -- do_home_joint(-1) -> do_home_all() -> the HOME_SEQUENCE state machine -- and, like the GUI Home All button, needs HOME_SEQUENCE set. The config has three single-joint sequence groups and NO_FORCE_HOMING=1 so the test can issue a bare G28.2 as a real first-home from MDI and from a program on an unreferenced machine. It checks all joints end up homed, in sequence order, with no spurious "did not complete" from the group-gap window, and the task mode untouched. position-model: pins the documented limitation that G28.2 does not resync the interpreter's current point. rs274 standalone: after "G0 X2 / G28.2 P0" a "G91 G1 X1" emits STRAIGHT_FEED to X3 (pre-home 2 + 1), and an I/J arc after a G28.2 takes its center from the pre-home point. If completion is ever changed to resync the position model, this expected output changes and the doc NOTE should go. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Suq9ikvpe4gvo7PJdjA49y
Implements the direction from PR LinuxCNC#4172's review discussion instead of the axis-letter form originally sketched there: - Adds an optional Pn word to G28.2/G28.3 to home/unhome a single joint by its 0-based joint number (matching [JOINT_n] INI numbering), e.g. G28.2 P1. Bare G28.2/G28.3 (no P) are unchanged (home/unhome all joints). Reuses the joint field EMC_JOINT_HOME/EMC_JOINT_UNHOME already carry, so it needs no NML change and works identically on any kinematics -- exactly the primitive grandixximo's review comment argued for. The axis-letter form (G28.2 X) is deliberately NOT implemented: resolving an axis letter to a joint needs the kinematics coordinate map and isn't trivial even on trivkins (duplicate letters on gantries), and andypugh's review also objected that homing is a joint concept, not an axis one. G28.2/G28.3 needed adding to the P-word whitelist in interp_check.cc (checked against g_modes[GM_MODAL_0], since they are modal-group-0 codes like G10/G4, not motion-group codes). - Fixes the real gap grandixximo's review identified: do_homing() (control.c) only ever advances while motion is in FREE mode, so a home/unhome issued from a running program or MDI while in TELEOP/COORD would previously either be rejected by a motion-side guard or silently never progress. Task now sequences it properly: a new EMC_TASK_EXEC::WAITING_FOR_HOMING state (modeled on the existing WAITING_FOR_SPINDLE_ORIENTED state) saves the current trajectory mode, dips into FREE, waits for the actual per-joint .homing/.homed status to reach the expected end state, then restores the prior mode -- invisibly to the task-level MDI/AUTO/MANUAL state, the same principle multichannel-DESIGN.txt uses for the analogous per-channel-homing problem. If homing/unhoming does not reach the expected end state, the program aborts (execState = ERROR) rather than continuing unreferenced, and the machine is left in FREE for an operator to intervene from rather than snapped back to a mode an unhomed machine may not legally run coordinated motion in. HOME and UNHOME are not symmetric at the motion level: EMCMOT_JOINT_HOME is a genuine state machine (.homing goes true while running), but EMCMOT_JOINT_UNHOME (command.c) is synchronous -- set_unhomed() just clears .homed immediately, .homing is never touched. The sequencing wait branches accordingly: UNHOME checks the target .homed state directly, HOME waits for the full start-then-finish cycle. - Re-applies [TRAJ]NO_FORCE_HOMING at the point a home/unhome command already forces a sync, closing the hole Sigma1912 raised in the PR discussion (G28.3 mid-program followed by a move with no re-home). NO_FORCE_HOMING=0 already refuses to *start* MDI/AUTO on an unhomed machine, but only at program/MDI start, not per line, so this specific gap needed its own check -- at no cost to the motion path, since it only runs at a sync point the command already forces. - Fixes two bugs found in the process that predate this commit and affect the base G28.2/G28.3/GCODE_HOMING feature, not just Pn: * HOME_CYCLE()/UNHOME_AXES()/HOME_CYCLE_IF_UNHOMED() (emccanon.cc) never flushed pending chained motion segments before appending their own command. STRAIGHT_FEED/STRAIGHT_TRAVERSE buffer points for arc-blend lookahead and only reach interp_list on a flush, so a queued move immediately before a G28.2/G28.3/homing-G28 could silently execute AFTER the home instead of before it. Fixed by calling flush_segments() first in all five home/unhome canon functions (the three pre-existing ones too). * emcJointHome()/emcJointUnhome() (taskintf.cc) returned 0 (success) for an out-of-range joint number, so an invalid Pn would silently report success instead of an error. - Adds stub implementations of the two new canon calls to gcodemodule.cc (the Python gcode module bindings), the third canon backend alongside emccanon.cc and saicanon.cc. Validated live in headless sim: the exact rehoming-a-shared-joint use case Sigma1912 described (cold-start home-all, move, mid-program unhome one joint, rehome it, move again) completes cleanly with zero errors; a NO_FORCE_HOMING=0 config confirms an unreferenced move after an unhome is correctly blocked with the intended error message; a plain move-then-home-all program confirms the flush_segments() ordering fix. Interp-level regression (tests/interp/gcode-homing/*, tests/interp/rotation/g28) passes 4/4, including a new joint-pword test case for the Pn parsing. Signed-off-by: chabron94 <chabron94@gmail.com>
…can't hold Addresses the first of grandixximo's findings on PR LinuxCNC#4172, and the failure Sigma1912 hit on real hardware (Mesa 7I95T gantry): "g28.3 p0" reported "all joints must be homed before going into coordinated mode", greyed out the GUI's mode controls, and left the machine needing F2 to recover. The G28.2/G28.3 sequencing dips motion into FREE (do_homing() only advances there) and restores the previous trajectory mode when the command finishes. That restore was gated only on the command having succeeded for its *target* joint. But a per-joint G28.2 Pn / G28.3 Pn can succeed for its own joint while leaving the machine as a whole unreferenced, and motion refuses to (re-)enter TELEOP or COORD in that state on non-identity kinematics -- switch_to_teleop_mode() (motion.c) and the EMCMOT_COORD case (command.c) both gate on "kinType != KINEMATICS_IDENTITY && !get_allhomed()". So the restore was silently rejected while task still reported DONE: the program advanced to the next line, motion had no valid frame for it, and the machine sat stranded in FREE. Sigma's third test is the same bug cascading -- the g28.3 p2 breaks the mode state, then the following g28 and g0 fail with "need to be enabled, in coord mode". Mirror motion's own condition before restoring, and fail the command cleanly (staying in FREE, with an operator error) instead of reporting success and stranding the operator. Identity kinematics are unaffected: motion permits the restore there, so the behaviour is unchanged for trivkins. Note this only reaches the buggy path with NO_FORCE_HOMING=1. With the default 0, the pre-existing NO_FORCE_HOMING re-check catches a partial unhome first -- which, together with the existing tests using trivkins, is why neither the sim tests nor Sigma's first test caught it. The kinematics type is read from emcStatus->motion.traj.kinematics_type rather than this file's static emcmotConfig: that copy is filled in once just before the main loop and never refreshed, so it goes stale as soon as switchkins changes kinematics at runtime (G43.4/G43.5). taskintf.cc re-reads the motion config whenever config_num changes and republishes it in status. Tests: adds gcode-homing/nonidentity-restore, which needs both knobs the existing coverage lacks -- corexykins (KINEMATICS_BOTH) and NO_FORCE_HOMING=1. The program is "G28.3 P0 / M64 P0 / M2"; the digital output is the witness, since it needs no coordinated motion and so would still run with the machine stuck in FREE. Verified the test actually catches the regression by temporarily reverting the fix and confirming it fails -- the interpreter never returns to idle, with dout0=1 proving the program had carried on past the G28.3 -- then restored the fix and confirmed it passes. Verified: tests/interp/gcode-homing (6/6), and the full tests/interp + tests/motion-logger suite (89/89, 1 pre-existing skip). One flush-order failure seen in an earlier sweep did not reproduce (89/89 on re-run, 8/8 in isolation including under load); it uses trivkins, where this change is a no-op by construction.
…ed path Addresses grandixximo's three remaining findings on PR LinuxCNC#4172. 1. Completion test (his main concern) The HOME completion test inferred "homing has stopped" by OR-ing the per-joint .homing flags. On a machine that homes in several HOME_SEQUENCE groups, the sequence machine finishes one group and can spend a cycle or more before the next group raises .homing, so there is a window in which every joint reads .homing == false while the machine is still homing. Task samples far coarser than the servo cycle, lands in that window, and scores a perfectly good home-all as "did not complete". motion/homing.c documents the same lag in its own words -- "The homing status variable turns false before homing_active state turns false" -- and guards against it internally for exactly this reason. Use motion's aggregate get_homing_is_active() instead. It was not published anywhere, so this plumbs it through as emcmot_status_t.homing_active -> EMC_MOTION_STAT::homing_active, mirroring jogging_active field for field. The per-joint OR is kept as a belt-and-braces term, since it can only extend the "still running" window, never shorten it. EMC_STAT is 8064 bytes against the 20480-byte emcStatus NML buffer, so the added field is free. Single-joint Pn and single-sequence machines never hit the gap, which is why neither the sim tests nor Sigma1912's hardware test 1 caught it. 2. Sequencing applied to immediate commands as well as queued ones EMC_TASK_EXEC::WAITING_FOR_HOMING is only ever reached through emcTaskCheckPostconditions(), which task calls only for commands taken off the interp_list. The GUI's Home and Unhome buttons, halui and linuxcncrsh all send immediate commands: they reach emcTaskIssueCommand() but nothing follows up. Applying the FREE-mode dip to them was a regression in two ways: - the dip was never undone, silently stranding the machine in joint mode; and - because the dip runs before the command is issued, an immediate unhome started succeeding from teleop, where motion deliberately refuses it ("must be in joint mode or disabled to unhome", EMCMOT_JOINT_UNHOME in command.c). The sequencing was quietly granting a permission upstream denies. Scope the whole sequencing to the queued path via issuingQueuedCommand, so immediate home/unhome behaves exactly as it did before this branch. 3. Volatile unhome scoring A volatile unhome (joint == -2) clears only the joints configured VOLATILE_HOME and leaves every other joint homed, so "no joint in range is still homed" would score a correct volatile unhome as a failure. Task cannot narrow the check to just the volatile joints: volatile_home lives in motion's private homing state (H[jno].volatile_home) and is not published in joint status -- the volatile_home in emc_nml.hh belongs to EMC_JOINT_SET_HOMING_PARAMS, a command message, not to EMC_JOINT_STAT. Guarded, but note this is defensive rather than a live fix: G28.3 only ever emits Pn >= 0 or -1, and with the sequencing now scoped to the queued path an immediate unhome(-2) does not reach the scoring either. It is here so a future queued command carrying -2 cannot be silently scored as a failure. Tests: adds gcode-homing/immediate-unhome-mode, asserting an immediate unhome from teleop is refused with the mode untouched, and an immediate home leaves the mode untouched. Verified it catches the regression by neutralising the queued-path scoping and confirming it fails ("immediate unhome from teleop went through (homed=[0, 1, 1])"), then restoring it and confirming it passes. Verified: tests/interp/gcode-homing (7/7) and the full tests/interp + tests/motion-logger + tests/abort suite (94/94, 1 pre-existing skip).
Per the review discussion on PR LinuxCNC#4172: land the explicit G28.2 / G28.3 (with Pn) now, and hold the GCODE_HOMING plain-G28 behaviour for its own PR once it has a real-machine sign-off. Sigma1912's opening question settled it. With NO_FORCE_HOMING=0 a program cannot be started on an unhomed machine at all, so on a homed machine a plain G28 is just the ordinary return move and the home-first branch is unreachable. The only way to reach it is to unhome mid-program -- which is precisely the path that broke on his gantry. A feature whose only reachable path is the broken one does not belong in this PR. Removes the flag and everything gated on it: - interpreter: FEATURE_GCODE_HOMING, the [RS274NGC]GCODE_HOMING INI read, and the G28 home-first branch in convert_home() - canon: HOME_CYCLE_IF_UNHOMED() -- the declaration plus the implementation in all three backends (emccanon.cc, saicanon.cc, gcodemodule.cc) - NML: the EMC_HOME_ALL_IF_UNHOMED (-3) sentinel - task: the sentinel resolution in EMC_JOINT_HOME_TYPE (target_joint is now const), and the stale comment in emcTaskCheckPostconditions() - docs: the g-code.adoc NOTE, the GCODE_HOMING cross-reference in the G28.2/G28.3 note, and the ini-config.adoc entry - tests: gcode-homing/homing-on and gcode-homing/homing-off, which existed only to cover the two settings of the flag, plus stale mentions in joint-pword and flush-order G28.2 / G28.3 (with Pn) are untouched -- they were always independent of the flag. `git grep` for GCODE_HOMING, EMC_HOME_ALL_IF_UNHOMED and HOME_CYCLE_IF_UNHOMED now returns nothing. Done as a removal on top rather than by rewriting history: a merge commit sits partway along this branch, so unpicking 86c58f6 in place would mean replaying the series and would churn commits the reviewers have already read. The net diff of the PR is the same either way. The removed work is preserved intact on the g28-gcode-homing branch for its own PR. Verified: full tests/interp + tests/motion-logger + tests/abort suite (92/92, 1 pre-existing skip). 92 rather than 94 because the two flag tests above are the ones removed.
emcJointHome()/emcJointUnhome() bounded the joint number by EMCMOT_MAX_JOINTS, the compile-time size of joints[], instead of by the machine's configured joint count. A number in between the two -- P5 on a five-joint machine -- therefore passed task, reached motion, and was silently dropped: do_home_joint() finds no joint of that number to start and reports nothing at all. Task then waited for a homing cycle that could never begin. With the machine parked in the FREE-mode dip taken for homing sequencing, it sat out the whole two-second start timeout -- the GUI jogging in joint mode meanwhile -- and finally reported the generic "G28.2 home did not start", which names neither the joint nor the real problem. Reported on real hardware (Mesa 7I95T gantry) in PR LinuxCNC#4172 and reproduced exactly in sim: motion_mode read FREE for 2.0s, then COORD again. Bound both entry points by TrajConfig.Joints and report through emcOperatorError() rather than rcs_print(), so an operator who typed "G28.2 P5" sees which numbers the machine actually has. Checked in task rather than in the interpreter because the joint count is not part of interpreter state, and because task covers every caller -- G-code, the GUI Home button, halui, linuxcncrsh -- not just G-code. The unhome path had a check in motion, but as "jno > all_joints", so the first unconfigured joint number fell through it into an unrelated complaint about extra joints; and because an unconfigured joint reads as not homed, task's synchronous "no joint in range is still homed" test would have scored that refusal as a successful unhome. New test tests/interp/gcode-homing/invalid-pword: on a fully homed corexykins machine, G28.2 P3 and G28.3 P7 must each be refused with an error naming the joint, within the start timeout rather than after it, leaving the trajectory mode untouched -- and a valid G28.2 P1 on the same machine must still home. Verified to fail without this fix (2.03s, generic message). Suite 6/6; tests/interp + tests/motion-logger 92/92.
No reviewer of PR LinuxCNC#4172 could name a use for a G-code unhome that something simpler does not already serve. Sigma1912 asked twice what a program would do with it -- if a joint is no longer trusted, G28.2 re-homes it directly, and the homing cycle fails on its own if anything is wrong -- and grandixximo concluded it only makes sense under [TRAJ]NO_FORCE_HOMING=1, where a numbered parameter carries the same "this joint is unreferenced" flag with no new G-code and no controller state change. It was also the only operation in this PR able to leave a running program on an unreferenced machine. Sigma's "g28.3 p0" on a real gantry unhomed a joint mid-program and left the controller in joint mode with the coordinated modes refused, recoverable only through the GUI. The machinery that made that safe -- the unhome branch of the completion test, the NO_FORCE_HOMING re-check at the sync point, the volatile unhome (-2) guard -- exists solely to contain a hazard the feature itself introduces, so dropping the feature drops all of it. G28.2, bare and with Pn, is unchanged. Unhoming stays available from the GUI, halui and linuxcncrsh, none of which can strand a program mid-run. Removed: G_28_3 and its gees[] slot, the UNHOME_AXES()/UNHOME_JOINT() canon ops and their three implementations, EMC_JOINT_UNHOME_TYPE from the queued-command precondition and postcondition paths, homingIsUnhome and the unhome half of WAITING_FOR_HOMING, and the nonidentity-restore test, which tested the wedge above. EMC_JOINT_UNHOME takes the immediate path only, as it did before this PR. tests/interp 88/88; gcode-homing + motion-logger + motion 13/13.
The out-of-range home error read
Cannot home invalid joint 5 (valid: 0..4, or -1 for all)
which sends the reader straight into a second error, because the
interpreter refuses a negative P word:
P value for G28.2 must be a non-negative whole joint number
-1 (all) and -2 (volatile) are an internal NML convention. They are how
the GUI's Home All button, halui and linuxcncrsh ask for those
operations; G-code cannot express them and is not meant to. Naming them
in an operator-facing message was documenting the API to someone holding
a G-code manual.
Report only what the reader can act on -- the joints this machine has --
and point at the spelling that does what they wanted:
Cannot home invalid joint 5 (this machine has joints 0..4; omit the
joint to home them all)
P value for G28.2 must be a non-negative whole joint number (omit P
to home every joint)
Reported by Sigma1912 on PR LinuxCNC#4172 after re-testing the P5 fix on real
hardware (Mesa 7I95T).
The unhome message loses its sentinels for the same reason, though that
path is reachable only from the interfaces where -1/-2 are valid input.
tests/interp/gcode-homing/invalid-pword now asserts that the rejection
names no negative sentinel, and that G28.2 P-1 is refused with an error
naming the bare form. Both assertions verified to fail against the
previous messages.
The directory was named when this branch also carried [RS274NGC]GCODE_HOMING (plain G28 references the machine first) and G28.3 (G-code unhome). Both were dropped from PR LinuxCNC#4172 -- GCODE_HOMING split to its own branch, G28.3 removed outright -- so every test here now exercises G28.2 and nothing else. Rename the directory to tests/interp/g28.2/, and the immediate-unhome-mode subdir to immediate-mode-guard: with G28.3 gone it no longer tests an unhome path specific to G-code, it checks that immediate (GUI/halui/ linuxcncrsh) home and unhome commands keep their pre-PR trajectory-mode behaviour. Pure rename, no content change. Nothing outside the directory referenced the old path.
…model Two gaps grandixximo pointed at in the PR LinuxCNC#4172 review: bare-home-sequence: every existing test drives G28.2 Pn. Bare G28.2 takes a different path -- do_home_joint(-1) -> do_home_all() -> the HOME_SEQUENCE state machine -- and, like the GUI Home All button, needs HOME_SEQUENCE set. The config has three single-joint sequence groups and NO_FORCE_HOMING=1 so the test can issue a bare G28.2 as a real first-home from MDI and from a program on an unreferenced machine. It checks all joints end up homed, in sequence order, with no spurious "did not complete" from the group-gap window, and the task mode untouched. position-model: pins the documented limitation that G28.2 does not resync the interpreter's current point. rs274 standalone: after "G0 X2 / G28.2 P0" a "G91 G1 X1" emits STRAIGHT_FEED to X3 (pre-home 2 + 1), and an I/J arc after a G28.2 takes its center from the pre-home point. If completion is ever changed to resync the position model, this expected output changes and the doc NOTE should go.
Homing only advances while motion_state == EMCMOT_MOTION_FREE (do_homing() is called from emcmotController() only there). The queued G28.2 path dips motion into FREE before issuing EMCMOT_JOINT_HOME, but for a cycle or two after the request motion_state can still read non-FREE while that transition settles, and the command was rejected with "must be in joint mode to home". Relax the guard just for that window: in position, nothing queued, and a FREE transition already pending (teleoperating and coordinating both cleared). An immediate home from halui / linuxcncrsh / c.home(n) on an all-homed machine sitting in TELEOP is idle too but has teleoperating set, so it is still refused exactly as before - accepting it there would silently drop it, since do_homing() would never run. (Found in review of PR LinuxCNC#4172.)
G28.2 lets a program or MDI line reference the machine instead of requiring the GUI Home All button. The bare form homes every joint in HOME_SEQUENCE order; an optional Pn homes joint n only (0-based [JOINT_n] numbering), reusing the existing EMC_JOINT_HOME 'joint' field so no NML change is needed and it works on any kinematics. Axis-letter forms are deliberately not supported - resolving a letter to a joint needs the kinematics map, and homing is a joint concept. There is no G-code unhome (dropped in review of PR LinuxCNC#4172). New canon calls HOME_CYCLE() / HOME_CYCLE_JOINT(n). The milltask backend flushes the segment buffer before queuing the home, or a move buffered for arc-blend lookahead would reorder after it. When the cycle finishes the interpreter resyncs its current position from the machine, the same way it does after probing or a tool change (home_flag -> INTERP_EXECUTE_FINISH -> refresh_actual_position() in read_inputs). An immediate home rewrites the joint coordinate to HOME_OFFSET even with no physical motion, so without the resync a following G91 move or an I/J/K arc centre is computed from the stale pre-home point - on a wrapped rotary head re-homed mid-program with G28.2 Pn, the axis would then sweep the whole error. (Raised in PR LinuxCNC#4172 review.)
|
For safety reasons, can we make G28.2 do nothing without a P value? And use P-1 for home all. |
G28.2 lets a program or MDI line reference the machine instead of requiring the GUI Home All button. It takes a mandatory P word saying what to home: P-1 homes every joint in HOME_SEQUENCE order, and P0, P1, ... home one joint (0-based [JOINT_n] numbering), reusing the existing EMC_JOINT_HOME 'joint' field so no NML change is needed and it works on any kinematics. There is deliberately no bare form. Homing drives joints onto their switches at homing speed, ignoring soft limits, from wherever the machine happens to be, so starting that on every joint is not something a line should do by being truncated or mistyped; G28.2 alone is an error, not a quiet no-op, so an operator can tell it from a cycle that ran. -2 (volatile unhome) and every other negative stay refused: they are NML sentinels, not G-code surface. (Raised in review of PR LinuxCNC#4172.) Axis-letter forms are deliberately not supported - resolving a letter to a joint needs the kinematics map, and homing is a joint concept. There is no G-code unhome (dropped in review of PR LinuxCNC#4172). New canon calls HOME_CYCLE() / HOME_CYCLE_JOINT(n). The milltask backend flushes the segment buffer before queuing the home, or a move buffered for arc-blend lookahead would reorder after it. When the cycle finishes the interpreter resyncs its current position from the machine, the same way it does after probing or a tool change (home_flag -> INTERP_EXECUTE_FINISH -> refresh_actual_position() in read_inputs). An immediate home rewrites the joint coordinate to HOME_OFFSET even with no physical motion, so without the resync a following G91 move or an I/J/K arc centre is computed from the stale pre-home point - on a wrapped rotary head re-homed mid-program with G28.2 Pn, the axis would then sweep the whole error. (Raised in PR LinuxCNC#4172 review.)
|
@BsAtHome agreed, and it is done on the branch ( One deliberate reading of "do nothing": the bare form is refused, not A quiet no-op would be indistinguishable from a homing cycle that ran, and the Everything else negative stays refused: Two knock-on fixes that would otherwise have contradicted the new rule:
Docs updated to match, including the error list. Nothing in the homing path, Tests: @Sigma1912 this changes the spelling you ran on the 7I95T: @grandixximo your approval predates this, so it needs another look. |
|
it conflicts due to recent changes, please rebase, sorry... |
The per-joint EMC_JOINT_STAT.homing flags briefly all read false in the gap between HOME_SEQUENCE groups while the machine is still homing (the same deassertion lag motion/homing.c documents for its own use). A non-realtime observer that samples coarser than the servo cycle - task, waiting for a G-code homing cycle to finish - can land in that gap and conclude homing has stopped. Expose get_homing_is_active() as emcmot_status_t.homing_active and carry it through EMC_MOTION_STAT so task can ask "is the homing state machine running" without OR-ing the per-joint flags.
Homing only advances while motion_state == EMCMOT_MOTION_FREE (do_homing() is called from emcmotController() only there). The queued G28.2 path dips motion into FREE before issuing EMCMOT_JOINT_HOME, but for a cycle or two after the request motion_state can still read non-FREE while that transition settles, and the command was rejected with "must be in joint mode to home". Relax the guard just for that window: in position, nothing queued, and a FREE transition already pending (teleoperating and coordinating both cleared). An immediate home from halui / linuxcncrsh / c.home(n) on an all-homed machine sitting in TELEOP is idle too but has teleoperating set, so it is still refused exactly as before - accepting it there would silently drop it, since do_homing() would never run. (Found in review of PR LinuxCNC#4172.)
A JOINT_HOME taken off the interp_list (from G28.2) needs more than the immediate path the GUI Home button uses: - drain prior motion first (WAITING_FOR_MOTION precondition); without a case here it fell through to ERROR and was dropped; - dip the trajectory mode to FREE for the cycle and restore the prior mode when it finishes, invisibly to the task-level MDI/AUTO/MANUAL state (new WAITING_FOR_HOMING exec state); - wait on the real completion signal - motion's aggregate homing_active plus the per-joint homed flags - not just the per-joint .homing flags, which gap between HOME_SEQUENCE groups; - on failure (aborted, faulted, or a partial home that leaves non-identity kinematics unable to re-enter coordinated motion) abort the program and leave the machine in FREE rather than report DONE and strand the operator. Only the queued path is gated on issuingQueuedCommand; an immediate home keeps its original pass-through behaviour, since nothing calls emcTaskCheckPostconditions() for it to undo a mode dip. emcJointHome()/emcJointUnhome() now range-check the joint number against the configured joint count (TrajConfig.Joints), not EMCMOT_MAX_JOINTS: a number between the two passed task, reached motion, and was silently dropped. Reported through emcOperatorError() so a "G28.2 P5" on a five-joint machine says what the machine actually has.
G28.2 lets a program or MDI line reference the machine instead of requiring the GUI Home All button. It takes a mandatory P word saying what to home: P-1 homes every joint in HOME_SEQUENCE order, and P0, P1, ... home one joint (0-based [JOINT_n] numbering), reusing the existing EMC_JOINT_HOME 'joint' field so no NML change is needed and it works on any kinematics. There is deliberately no bare form. Homing drives joints onto their switches at homing speed, ignoring soft limits, from wherever the machine happens to be, so starting that on every joint is not something a line should do by being truncated or mistyped; G28.2 alone is an error, not a quiet no-op, so an operator can tell it from a cycle that ran. -2 (volatile unhome) and every other negative stay refused: they are NML sentinels, not G-code surface. (Raised in review of PR LinuxCNC#4172.) Axis-letter forms are deliberately not supported - resolving a letter to a joint needs the kinematics map, and homing is a joint concept. There is no G-code unhome (dropped in review of PR LinuxCNC#4172). New canon calls HOME_CYCLE() / HOME_CYCLE_JOINT(n). The milltask backend flushes the segment buffer before queuing the home, or a move buffered for arc-blend lookahead would reorder after it. When the cycle finishes the interpreter resyncs its current position from the machine, the same way it does after probing or a tool change (home_flag -> INTERP_EXECUTE_FINISH -> refresh_actual_position() in read_inputs). An immediate home rewrites the joint coordinate to HOME_OFFSET even with no physical motion, so without the resync a following G91 move or an I/J/K arc centre is computed from the stale pre-home point - on a wrapped rotary head re-homed mid-program with G28.2 Pn, the axis would then sweep the whole error. (Raised in PR LinuxCNC#4172 review.)
Docs: new "G28.2 Home from G-code" section in g-code.adoc (syntax, the mandatory P word and why there is no bare form, the HOME_SEQUENCE requirement for P-1, the negative/positive shared-sequence Pn cases, the mode-dip and position-resync behaviour, error conditions); G28.2 added to the modal-group-0 lists in overview.adoc, remap.adoc, gcode.html.in, hal_glib.py and mdi_text.py, with the qtvcp MDI help carrying the same P-word rules. Tests (tests/interp/g28.2/): joint-pword and invalid-pword drive rs274 / task directly; the rest run a sim under milltask - - sequencing: the FREE-mode dip is invisible at the task level; an invalid Pn does not wedge task.mode; - immediate-mode-guard: an immediate home from teleop is still refused; - home-all-sequence: G28.2 P-1 as a first-home on a HOME_SEQUENCE config, and a bare G28.2 refused without homing anything; - invalid-pword: the rest of the P word's surface - an unconfigured joint number, P-2, another negative and a fraction are all refused up front, name P-1 as the way to home everything, and leave the homed state and trajectory mode alone; - flush-order: a move before G28.2 runs before the home; - position-model: a G91 move (rotary C and linear X) after G28.2 Pn is computed from the homed position, not the stale one - fails without the resync (lands at stale+increment).


G28.2 — home the machine from G-code
Lets a program or MDI line reference the machine, instead of requiring the
operator to press Home All in the GUI.
A
Pword is mandatory and says what to home:G28.2 P-1— run the homing cycle on all joints, inHOME_SEQUENCEorder (the same operation as the GUI's Home All, and like Home All it
needs
HOME_SEQUENCEset)G28.2 Pn— run the homing cycle on jointnonly, wherenis the0-based joint number matching its
[JOINT_n]INI sectionG28.2alone — an error. Homing drives joints onto their switches athoming speed, ignoring soft limits, from wherever the machine happens to
be, so homing the whole machine has to be asked for explicitly rather than
being what a truncated or mistyped line does (@BsAtHome). It is refused
rather than ignored, so it cannot be mistaken for a cycle that ran.
P-2(the volatile-unhome sentinelEMC_JOINT_HOMEcarries for the GUI,halui and linuxcncrsh), any other negative, and fractional values are
refused too:
-1is G-code surface,-2is not.G28.2is non-modal (modal group 0), following the existingG28.1/G30.1pattern. It is a LinuxCNC extension; there is no standard Fanucequivalent.
What it's for
reference the machine without a human at the GUI.
below: a joint switched between rotary-axis and spindle use, whose
reference is no longer valid once it has run as a spindle.
G28.2 Pnre-establishes it in place, which also avoids the workaround that
issue Connecting joint.n.index-enable to spindle.n.index-enable breaks spindle-synchronized motion #3556 currently forces.
PndetailsThe joint index rides the
jointfield thatEMC_JOINT_HOMEalreadycarries, so this needs no NML change and no motion change, and behaves
identically on any kinematics.
How many joints
Pnactually homes depends on jointn'sHOME_SEQUENCE:—
Pnon either joint of a gantry pair homes both (motion's existinggantry behavior);
n—use
G28.2 P-1to home the shared group together;joint
n.Pnis range-checked in task against the machine's configured joint count,and an out-of-range number is refused before anything else happens:
There is deliberately no axis-letter form (
G28.2 X): resolving an axisletter to a joint needs the kinematics coordinate map and is ambiguous even
on trivkins (duplicate letters on gantries), and as @andypugh noted, homing
is a joint concept rather than an axis one. An axis word with
G28.2is anerror.
How it works
interpconvert_home_cycle()emits the canon opHOME_CYCLE(), orHOME_CYCLE_JOINT(n)when aPword is present.canonflushes queued segments and appendsEMC_JOINT_HOMEwith thejoint number (
-1for all).taskexecutes it in program order and waits for the cycle to finishbefore letting the program continue.
interpthen resyncs its model of the current position from the machine(see Position model below) and read-ahead resumes.
Homing only advances while motion is in FREE mode (
do_homing()is calledfrom there), so a home queued from a program or MDI running in TELEOP/COORD
would otherwise stall silently. Task dips motion into FREE for the duration
and restores the previous trajectory mode when the cycle completes —
invisibly to the task-level MANUAL/MDI/AUTO state.
Three details of that sequencing are worth calling out for review:
homing_active, notagainst an OR of the per-joint
.homingflags. On a machine that homes inseveral
HOME_SEQUENCEgroups, the sequence machine finishes one group andcan spend a cycle or more before the next raises
.homing, so there is awindow where every joint reads
.homing == falsewhile the machine isstill homing. Task samples far coarser than the servo cycle and can land in
it.
motion/homing.cdocuments the same deassertion lag in its own words.This is what the new
homing_activestatus field is for.all_homed()for non-identitykinematics, mirroring the condition motion itself applies in
switch_to_teleop_mode()and theEMCMOT_COORDcase. Restoringunconditionally would have task report DONE while motion refused the
transition, leaving the machine in FREE with the GUI's mode controls dead.
the GUI Home button, halui, linuxcncrsh — pass straight through exactly
as before, since nothing calls
emcTaskCheckPostconditions()for them anda mode dip taken there would never be undone.
Position model
When the cycle finishes, the interpreter resyncs
current_*from themachine — the same mechanism it uses after probing or a tool change: a
home_flagmakesexecute_block()returnINTERP_EXECUTE_FINISH(soG28.2is a read-ahead barrier), and
read_inputs()then callsrefresh_actual_position(). Nosynch()— no tool-table reload, noparameter-file rewrite.
This matters because an immediate (index/switchless) home rewrites the
joint's coordinate to
HOME_OFFSETeven when nothing physically moves(
HOME_SET_INDEX_POSITION). Without the resync a followingG91move, or anarc centre given with
I/J/K, is computed from the stale pre-home point;on a wrapped rotary head re-homed mid-program with
G28.2 Pn— the headlineuse case — the axis would then sweep the whole error. (Raised in review.)
G5x/G92offsets that were active before theG28.2stay appliedafterward and now refer to the newly established machine zero — documented,
not silently changed.
Supporting fixes
Both are needed for a queued home to reach motion at all:
EMC_JOINT_HOME_TYPEwas missing fromemcTaskCheckPreconditions(), so a queued home hit thedefaultcase andwas silently dropped before ever reaching motion. It now returns
WAITING_FOR_MOTION(drain prior motion, then home).motion_state == FREE. It is now alsopermitted when motion is in position with an empty queue and a FREE
transition is already pending (
!teleoperating && !coordinating) — thetransient state the queued G28.2 dip passes through. An immediate home
from TELEOP/COORD is still refused with must be in joint mode to home,
exactly as before this PR (this was tightened after review found the first
version also silently accepted an immediate
c.home(n)/ halui /linuxcncrsh home on an already-homed machine).
Also included:
homing_activeadded toemcmot_status_tandEMC_MOTION_STAT(with its constructor initializer and NML serializerentry), and a new
EMC_TASK_EXEC::WAITING_FOR_HOMINGstate.Documentation
G28.2is added to the quick-reference index (gcode.html.in), themodal-group table and the allocated-G-codes table, the qtvcp MDI help, and
hal_glib's modal-group map, plus its own section ing-code.adoccoveringthe syntax, why the
Pword is mandatory, theHOME_SEQUENCErequirementfor
P-1, thenegative/positive shared-sequence
Pncases, the mode-dip andposition-resync behaviour, and an error list matched to the interpreter.
Deliberately not in this PR
G28.3(unhome) was dropped after review. No reviewer could name a usea numbered parameter would not serve better under
NO_FORCE_HOMING=1, andit was the only operation here able to leave a running program on an
unreferenced machine. Unhoming remains available from the GUI, halui and
linuxcncrsh.
[RS274NGC]GCODE_HOMING=1(plainG28references the machine first)was split out and will be proposed separately. It needs real-machine
validation of the home-then-coordinated-return sequence that sim cannot
fully exercise.
Testing
Seven test cases under
tests/interp/g28.2/:joint-pword,sequencing,flush-order,invalid-pword,immediate-mode-guard,home-all-sequence,position-model.home-all-sequence—G28.2 P-1(first-home and from a program) on aconfig with three
HOME_SEQUENCEgroups; homes every joint in order, nospurious group-gap failure. A bare
G28.2is refused and homes nothing.invalid-pword— the rest of thePsurface: an unconfigured jointnumber,
P-2, another negative, a fraction and the bare form are eachrefused up front, each name
P-1, and leave the homed state and thetrajectory mode untouched.
immediate-mode-guard— an immediate home/unhome from teleop is refusedwith the mode untouched (regression test for the idle-home tightening).
position-model— aG91move (rotary C and linear X) afterG28.2 Pnis computed from the homed position; fails without the resync (lands at
stale + increment).tests/interp/g28.2: 7/7.tests/interp98/98,tests/motion-logger,tests/motion,tests/abortandtests/remap: green.cppcheckclean.Every one of the 5 commits builds with
--enable-werror.Real hardware (@Sigma1912, Mesa 7I95T, gantry):
g28.2(now spelledg28.2 p-1) homes alljoints;
g28.2 p0homes the gantry pair;g28.2 p4homes the named joint;and a program of the form
g0 x100 / g01 a-5 / g28.2 p0 / g01 x50runs thefirst moves, re-homes the gantry, then completes — all correct. (Run
against an earlier revision; a re-run against the current branch is
welcome.)
Credits
Thanks to everyone who reviewed earlier revisions and took this to real
hardware — the
Pnper-joint form, theG28.3removal, the joint-vs-axisdecision and the position-model fix all came out of that feedback.