Skip to content

Commit 7795ad3

Browse files
committed
docs, tests: document and cover G28.2
Docs: new "G28.2 Home from G-code" section in g-code.adoc (syntax, the HOME_SEQUENCE requirement for the bare form, 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. 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; - bare-home-sequence: the bare form as a first-home on a HOME_SEQUENCE config; - 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).
1 parent cd3497b commit 7795ad3

42 files changed

Lines changed: 1625 additions & 3 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/src/gcode.html.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ tr.head td, tr.head th { background: black; color: white; }
165165
<tr><td><a href="gcode/g-code.html#gcode:g10-l2">G10 L2</a></td><td>P R</td><td>Set Coordinate System</td></tr>
166166
<tr><td><a href="gcode/g-code.html#gcode:g10-l20">G10 L20</a></td><td>P</td><td>Set Coordinate System</td></tr>
167167
<tr><td><a href="gcode/g-code.html#gcode:g28-g28.1">G28, G28.1</a></td><td/><td>Go/Set Predefined Position</td></tr>
168+
<tr><td><a href="gcode/g-code.html#gcode:g28.2">G28.2</a></td><td>P</td><td>Home from G-code</td></tr>
168169
<tr><td><a href="gcode/g-code.html#gcode:g30-g30.1">G30, G30.1</a></td><td/><td>Go/Set Predefined Position</td></tr>
169170
<tr><td><a href="gcode/g-code.html#gcode:g53">G53</a></td><td/><td>Move in Machine Coordinates</td></tr>
170171
<tr><td><a href="gcode/g-code.html#gcode:g92">G52, G92</a></td><td/><td>Coordinate System Offset</td></tr>

docs/src/gcode/g-code.adoc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ as the 'L number', and so on for any other letter.
7373
|<<gcode:g17-g19.1,G17 - G19.1>> |Plane Select
7474
|<<gcode:g20-g21,G20 G21>> |Set Units of Measure
7575
|<<gcode:g28-g28.1,G28 - G28.1>> |Go to Predefined Position
76+
|<<gcode:g28.2,G28.2>> |Home from G-code
7677
|<<gcode:g30-g30.1,G30 - G30.1>> |Go to Predefined Position
7778
|<<gcode:g33,G33>> |Spindle Synchronized Motion
7879
|<<gcode:g33.1,G33.1>> |Rigid Tapping
@@ -997,6 +998,73 @@ It is an error if :
997998

998999
* Cutter Compensation is turned on
9991000

1001+
[[gcode:g28.2]]
1002+
== G28.2 Home from G-code(((G28.2 Home from G-code)))
1003+
1004+
This non-modal code lets a program or MDI line reference the machine
1005+
instead of requiring the operator to use the GUI's *Home All* button. It
1006+
follows the same modal-group-0 pattern as `G28.1`/`G30.1` and takes no axis
1007+
words.
1008+
1009+
* 'G28.2' - runs the homing cycle on every joint, in `HOME_SEQUENCE` order
1010+
(the same operation as the GUI *Home All*). The bare form requires the
1011+
config to set `[JOINT_n]HOME_SEQUENCE`; exactly as with *Home All*, a
1012+
config with no `HOME_SEQUENCE` cannot home all joints at once and the
1013+
command fails, leaving the machine in joint mode.
1014+
* 'G28.2 Pn' - runs the homing cycle on joint 'n' only, where 'n' is the
1015+
0-based joint number matching its `[JOINT_n]` INI section (the same
1016+
numbering used by `HOME_SEQUENCE` and by joint jogging). Other joints are
1017+
left as they are, and `HOME_SEQUENCE` is not required for the `Pn` form.
1018+
How many joints actually home depends on the `HOME_SEQUENCE` of joint 'n':
1019+
+
1020+
--
1021+
** a *negative* (synchronized) sequence homes the whole synchronized group -
1022+
`Pn` on either joint of a gantry pair homes both;
1023+
** a *positive* sequence shared with other joints homes only joint 'n' -
1024+
use the bare `G28.2` to home the shared group together;
1025+
** no sequence, or a sequence not shared with any other joint, homes just
1026+
joint 'n'.
1027+
--
1028+
1029+
.G28.2 Example Lines
1030+
[source,ngc]
1031+
----
1032+
G28.2 (home all joints, in HOME_SEQUENCE order)
1033+
G28.2 P1 (home joint 1 only)
1034+
----
1035+
1036+
A queued `G28.2` dips motion into free mode for the duration of the homing
1037+
cycle and restores whatever mode (manual/MDI/auto) was active once it
1038+
finishes, so the mode dip is invisible at the task level. Motion still
1039+
enforces its own safety: the home is honored only when the machine is idle
1040+
(in position with no queued motion) or in joint mode, and a home is refused
1041+
mid-motion. Homing inhibits and per-joint limit handling are unchanged.
1042+
1043+
[NOTE]
1044+
When the homing cycle finishes, the interpreter resyncs its model of the
1045+
current position from the machine (the same way it does after probing or a
1046+
tool change), so a following move made in incremental distance mode
1047+
(`G91`), or an arc whose center is given with `I`/`J`/`K`, is computed from
1048+
where the machine actually is after homing. `G28.2` is a queue point: read
1049+
ahead stops at it and resumes once the cycle has run. `G5x`/`G92` offsets
1050+
that were active before the `G28.2` are still applied afterward and now
1051+
refer to the newly established machine zero; clear or re-establish them if
1052+
that is not what you want.
1053+
1054+
[NOTE]
1055+
There is no G-code unhome. Clearing a joint's reference is done from the
1056+
GUI, halui or linuxcncrsh.
1057+
1058+
[NOTE]
1059+
`G28.2` is a LinuxCNC extension; there is no standard Fanuc equivalent.
1060+
1061+
It is an error if:
1062+
1063+
* Cutter radius compensation is active (`G41`, `G41.1`, `G42` or `G42.1`).
1064+
* An axis word is present ('G28.2' takes no axis words).
1065+
* The 'P' word is negative or is not a whole number.
1066+
* The 'P' word names a joint number the machine does not have.
1067+
10001068
[[gcode:g30-g30.1]]
10011069
== G30, G30.1 Go/Set Predefined Position(((G30 Go/Set Predefined Position)))
10021070

docs/src/gcode/overview.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,7 @@ The modal groups are shown in the following Table.
960960
[width="80%",cols="4,6",options="header"]
961961
|===
962962
|Modal Group Meaning | Member Words
963-
|Non-modal codes (Group 0) | G4, G10 G28, G30, G52, G53, G92, G92.1, G92.2, G92.3,
963+
|Non-modal codes (Group 0) | G4, G10 G28, G28.2, G30, G52, G53, G92, G92.1, G92.2, G92.3,
964964
|Motion (Group 1) | G0, G1, G2, G3, G33, G38.n, G73, G76, G80, G81
965965
G82, G83, G84, G85, G86, G87, G88, G89
966966
|Plane selection (Group 2) | G17, G18, G19, G17.1, G18.1, G19.1

docs/src/remap/remap.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,7 @@ All the listed G-codes are already defined in the current implementation of Linu
18511851
|25 | | | | | | | | | |
18521852
|26 | | | | | | | | | |
18531853
|27 | | | | | | | | | |
1854-
|28 |G28 |G28.1 | | | | | | | |
1854+
|28 |G28 |G28.1 |G28.2 | | | | | | |
18551855
|29 | | | | | | | | | |
18561856
|===
18571857

lib/python/common/hal_glib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ class _GStat(GObject.GObject):
287287
STATE_ON = linuxcnc.STATE_ON
288288
STATE_OFF = linuxcnc.STATE_OFF
289289

290-
group0 = ('G4', 'G10','G28', 'G30', 'G52', 'G53', 'G92', 'G92.1', 'G92.2', 'G92.3')
290+
group0 = ('G4', 'G10','G28', 'G28.2', 'G30', 'G52', 'G53', 'G92', 'G92.1', 'G92.2', 'G92.3')
291291
group1 = ('G0', 'G1', 'G2', 'G3', 'G33', 'G38.n', 'G73', 'G76', 'G80', 'G81',\
292292
'G82', 'G83', 'G84', 'G85', 'G86', 'G87', 'G88', 'G89')
293293
group2 = ('G17', 'G18', 'G19', 'G17.1', 'G18.1', 'G19.1')

lib/python/qtvcp/lib/mdi_text.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def gcode_titles():
2727
'G21': 'Set Units to Millimeters',
2828
'G28': 'Go to Predefined Position',
2929
'G28.1': 'Set Predefined Position',
30+
'G28.2': 'Home from G-code',
3031
'G30': 'Go to Predefined Position',
3132
'G30.1': 'Set Predefined Position',
3233
'G33': 'Spindle Synchronized Motion',
@@ -209,6 +210,7 @@ def gcode_descriptions(gcode):
209210
'G21': G21,
210211
'G28': G28,
211212
'G28.1': G28_1,
213+
'G28.2': G28_2,
212214
'G30': G30,
213215
'G30.1': G30_1,
214216
'G33': G33,
@@ -467,6 +469,24 @@ def gcode_descriptions(gcode):
467469
G28.1 - stores the current absolute position into
468470
parameters 5161-5166.
469471
"""
472+
473+
G28_2 = """G28.2 Home from G-code
474+
Runs the homing cycle, the same operation as the GUI
475+
Home All button, so a program or MDI line can
476+
reference the machine. Takes no axis words.
477+
478+
G28.2 - homes all joints, in HOME_SEQUENCE order
479+
(the config must set HOME_SEQUENCE).
480+
481+
G28.2 Pn - homes joint n only, where n is the
482+
0-based joint number matching its [JOINT_n] INI
483+
section. On a synchronized (negative HOME_SEQUENCE)
484+
joint pair, Pn on either joint homes both.
485+
486+
It is an error if cutter compensation is on, or if
487+
Pn names a joint the machine does not have.
488+
"""
489+
470490
G30 = """G30 Go to Predefined Position
471491
G30 uses the values stored in parameters 5181-5189
472492
as the X Y Z A B C U V W final point to move to.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
exit 0 # test failure is indicated by test.sh exit value

tests/interp/g28.2/bare-home-sequence/sim.tbl

Whitespace-only changes.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Bare G28.2 (no P word) on a config that sets HOME_SEQUENCE.
5+
6+
The other tests here only exercise G28.2 Pn. Bare G28.2 goes through a
7+
different motion path -- do_home_joint(-1) -> do_home_all() -> the
8+
HOME_SEQUENCE state machine -- and needs HOME_SEQUENCE set, exactly like
9+
the GUI Home All button. This config has three single-joint sequence
10+
groups (0, 1, 2).
11+
12+
Checks:
13+
1. Bare G28.2 issued from MDI homes every joint and completes without a
14+
task error.
15+
2. The task-level mode is not disturbed by the FREE-mode dip.
16+
3. Homing walks the sequence groups in order (joint 0, then 1, then 2) --
17+
and the completion poll does not trip on the gap between groups, where
18+
all per-joint .homing flags read false while the machine is still
19+
homing. A false "did not complete" would abort the MDI command and
20+
leave the machine unhomed in FREE.
21+
4. Bare G28.2 from a program (AUTO) works the same way.
22+
"""
23+
24+
import linuxcnc
25+
import hal
26+
27+
import sys
28+
import time
29+
30+
h = hal.component("python-ui")
31+
h.ready()
32+
33+
c = linuxcnc.command()
34+
s = linuxcnc.stat()
35+
e = linuxcnc.error_channel()
36+
37+
38+
def poll():
39+
s.poll()
40+
41+
42+
def fail(msg):
43+
print("FAIL: " + msg)
44+
sys.exit(1)
45+
46+
47+
def drain_errors():
48+
msgs = []
49+
while True:
50+
err = e.poll()
51+
if not err:
52+
return msgs
53+
msgs.append(err[1])
54+
55+
56+
def wait_idle(timeout=15.0):
57+
t0 = time.time()
58+
while time.time() - t0 < timeout:
59+
poll()
60+
if s.exec_state == linuxcnc.EXEC_DONE and s.interp_state == linuxcnc.INTERP_IDLE:
61+
return True
62+
time.sleep(0.01)
63+
return False
64+
65+
66+
def wait_homed(expected, timeout=15.0):
67+
t0 = time.time()
68+
while time.time() - t0 < timeout:
69+
poll()
70+
if list(s.homed[:3]) == expected:
71+
return True
72+
time.sleep(0.01)
73+
return False
74+
75+
76+
c.state(linuxcnc.STATE_ESTOP_RESET)
77+
c.state(linuxcnc.STATE_ON)
78+
poll()
79+
if list(s.homed[:3]) != [0, 0, 0]:
80+
fail("machine came up homed: {}".format(list(s.homed[:3])))
81+
82+
c.mode(linuxcnc.MODE_MDI)
83+
time.sleep(0.2)
84+
poll()
85+
mode_before = s.task_mode
86+
drain_errors()
87+
88+
# --- 1: bare G28.2 from MDI homes everything -------------------------------
89+
c.mdi("G28.2")
90+
91+
# Watch the homed flags flip on in sequence order while the cycle runs.
92+
order = []
93+
t0 = time.time()
94+
while time.time() - t0 < 15.0:
95+
poll()
96+
for j in range(3):
97+
if s.homed[j] and j not in order:
98+
order.append(j)
99+
if list(s.homed[:3]) == [1, 1, 1]:
100+
break
101+
time.sleep(0.005)
102+
103+
if list(s.homed[:3]) != [1, 1, 1]:
104+
fail("bare G28.2 did not home all joints: homed={}".format(list(s.homed[:3])))
105+
if order != [0, 1, 2]:
106+
fail("joints did not home in HOME_SEQUENCE order: saw {}".format(order))
107+
108+
if not wait_idle():
109+
fail("bare G28.2 (MDI) did not return to idle/DONE")
110+
111+
errs = drain_errors()
112+
if any("did not complete" in m or "did not start" in m for m in errs):
113+
fail("bare G28.2 reported a spurious homing failure: {!r}".format(errs))
114+
poll()
115+
if s.exec_state == linuxcnc.EXEC_ERROR:
116+
fail("bare G28.2 left task in EXEC_ERROR (errors={!r})".format(errs))
117+
if s.task_mode != mode_before:
118+
fail("task_mode changed across bare G28.2: {} -> {}".format(mode_before, s.task_mode))
119+
print("PASS: bare G28.2 from MDI homes every joint in sequence order, mode untouched")
120+
121+
# --- 2: and again from a program -----------------------------------------
122+
c.mode(linuxcnc.MODE_MANUAL)
123+
c.teleop_enable(0) # to joint mode, so the unhome is allowed
124+
time.sleep(0.3)
125+
c.unhome(-1)
126+
if not wait_homed([0, 0, 0]):
127+
fail("could not unhome for the AUTO re-test: {}".format(list(s.homed[:3])))
128+
129+
c.mode(linuxcnc.MODE_AUTO)
130+
time.sleep(0.2)
131+
c.program_open("test.ngc")
132+
time.sleep(0.2)
133+
drain_errors()
134+
c.auto(linuxcnc.AUTO_RUN, 0)
135+
136+
if not wait_homed([1, 1, 1]):
137+
fail("bare G28.2 from a program did not home all joints: {}".format(list(s.homed[:3])))
138+
if not wait_idle():
139+
fail("program with bare G28.2 did not finish")
140+
errs = drain_errors()
141+
if errs:
142+
fail("program with bare G28.2 raised errors: {!r}".format(errs))
143+
print("PASS: bare G28.2 from a program homes every joint")
144+
145+
print("done! it all worked")
146+
sys.exit(0)

0 commit comments

Comments
 (0)