At fest we’ve been talking about part programs that cause AXIS to spuriously warn that the program may exceed the machine’s soft limits.
One class of problem results from part programs that don’t begin with a set of preparatory G0 moves that take the controlled point to a fixed location with respect to the coordinate system origin, such as one which begins with a G1 move (so that no axes are at an established position before the first cut) , or which begin G0 X- Y- / G1 Z... (so that only 2 of 3+ axes are at an established position before the first cut), or begin G91 G0 X- Y- Z- (so that all axis letters are specified, but beacuse the move is relative it still doesn’t establish an absolute location) or G38.2 X- Y- Z- or G0 X- Y- / G38.2 Z- so that one or more axes are only established by the result of a probing move.
For want of a better term, I’m calling these programs “ungrounded”, and I’ve written a small standalone program that appears to do an OK job of determining if a program is grounded or not.
For an ungrounded program we would (A) need to generate the preview starting from the current location, rather than the coordinate system, and (B) show an admonition to the user when such a program is loaded and they jog, that the preview may be out of date. (unlike automatically loading after touch off, loading after the end of any jog is too heavyweight to be feasible, so an admonition is probably a better idea)
import gcode
import sys
from rs274.interpret import Translated, ArcsToSegmentsMixinclass FirstMove(Exception):
def init(self, coords, is_probe):
Exception.init(self, ""
% (coords, is_probe))
self.coords = coords
self.isprobe = isprobe
class GroundedCanon(Translated, ArcsToSegmentsMixin):
def comment(*args): pass
def next_line(*args): pass
def setg5xoffset(*args): pass
def setg92offset(*args): pass
def setxyrotation(*args): pass
def getexternalangular_units(self): return 1.0
def getexternallength_units(self): return 1.0
def set_plane(*args): pass
def getaxismask(self): return 7
def get_tool(self, tool):
return tool, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0
def setfeedrate(self, rate): pass
def userdefinedfunction(self, m, p, q): pass
def straight_traverse(self, *coords): pass
def straight_feed(self, *coords):
raise FirstMove(coords, True)
def straight_probe(self, *coords):
raise FirstMove(coords, False)
def getfirsttraverse(filename, origin):
canon = GroundedCanon()
canon.parameter_file = "/dev/null"
try:
result, seq = gcode.parse(filename, canon, "", "", "", origin)
except FirstMove as e:
print origin, e
return e.coords, e.is_probe
else:
return (0.0,) * 9, False
def is_grounded(filename, axes=(0,1,2)):
s = set()
for idx in axes:
origin = tuple(1 if idx == i else 0 for i in range(9))
s.add(getfirsttraverse(filename, origin))
print >>sys.stderr, filename, len(s), s
return len(s) == 1
if name == 'main':
for fn in sys.argv[1:]:
print "%s:0: %s" % (fn, "grounded" if is_grounded(fn) else "ungrounded")
This depends on a small change to gcodemodule too
diff --git a/src/emc/rs274ngc/gcodemodule.cc b/src/emc/rs274ngc/gcodemodule.cc
index c4e9382..1788b73 100644
--- a/src/emc/rs274ngc/gcodemodule.cc
+++ b/src/emc/rs274ngc/gcodemodule.cc
@@ -687,7 +687,12 @@ static PyObject parse_file(PyObject self, PyObject *args) {
int errorlineoffset = 0;
struct timeval t0, t1;
int wait = 1;
- if(!PyArg_ParseTuple(args, "sO|sss", &f, &callback, &unitcode, &initcode, &interpname))
+
+ posx = posy = posz = posa = posb = posc = 0;
+ posu = posv = posw = 0;
+
+ if(!PyArg_ParseTuple(args, "sO|sss(ddddddddd)", &f, &callback, &unitcode, &initcode, &interpname,
+ &posx, &posy, &posz, &posa, &posb, &posc, &posu, &posv, &posw))
return NULL; if(pinterp) {
@@ -708,9 +713,6 @@ static PyObject parse_file(PyObject self, PyObject *args) {
interp_error = 0;
lastsequencenumber = -1;
- posx = posy = posz = posa = posb = posc = 0;
- posu = posv = posw = 0;
-
interp_new.init();
interp_new.open(f);