mirror of
https://git.mirrors.martin98.com/https://github.com/petaflot/pygcode
synced 2025-07-28 10:02:01 +08:00
updated readme
fixed canned drilling cycle machine processing bugs improved tests for linear movement
This commit is contained in:
parent
b4b71daae8
commit
aef2728b0d
10
README.rst
10
README.rst
@ -111,15 +111,13 @@ To elaborate, here are some line examples
|
|||||||
G01 X1 Y2 F100 S1000 ; blah
|
G01 X1 Y2 F100 S1000 ; blah
|
||||||
>>> print(line.block)
|
>>> print(line.block)
|
||||||
G01 X1 Y2 F100 S1000
|
G01 X1 Y2 F100 S1000
|
||||||
|
>>> sorted(line.block.gcodes)
|
||||||
|
[<GCodeFeedRate: F100>,
|
||||||
|
<GCodeSpindleSpeed: S1000>,
|
||||||
|
<GCodeLinearMove: G01{X1, Y2}>]
|
||||||
>>> print(line.comment)
|
>>> print(line.comment)
|
||||||
; blah
|
; blah
|
||||||
|
|
||||||
>>> line = Line('G0 x1 y2 (foo) f100 (bar) s1000')
|
|
||||||
>>> print(line)
|
|
||||||
G00 X1 Y2 F100 S1000 (foo. bar)
|
|
||||||
>>> print(line.comment)
|
|
||||||
(foo. bar)
|
|
||||||
|
|
||||||
|
|
||||||
Interpreting what a line of gcode does depends on the machine it's running on,
|
Interpreting what a line of gcode does depends on the machine it's running on,
|
||||||
and also that machine's state (or 'mode')
|
and also that machine's state (or 'mode')
|
||||||
|
@ -218,15 +218,29 @@ class GCode(object):
|
|||||||
return copy(self.word_key)
|
return copy(self.word_key)
|
||||||
raise AssertionError("class %r has no default word" % self.__class__)
|
raise AssertionError("class %r has no default word" % self.__class__)
|
||||||
|
|
||||||
# Comparisons
|
# Equality
|
||||||
|
def __eq__(self, other):
|
||||||
|
return (
|
||||||
|
(self.word == other.word) and
|
||||||
|
(self.params == other.params)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
# Sort by execution order
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
"""Sort by execution order"""
|
|
||||||
return self.exec_order < other.exec_order
|
return self.exec_order < other.exec_order
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return self.exec_order <= other.exec_order
|
||||||
|
|
||||||
def __gt__(self, other):
|
def __gt__(self, other):
|
||||||
"""Sort by execution order"""
|
|
||||||
return self.exec_order > other.exec_order
|
return self.exec_order > other.exec_order
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self.exec_order >= other.exec_order
|
||||||
|
|
||||||
# Parameters
|
# Parameters
|
||||||
def add_parameter(self, word):
|
def add_parameter(self, word):
|
||||||
"""
|
"""
|
||||||
@ -483,10 +497,18 @@ class GCodeCannedCycle(GCode):
|
|||||||
if isinstance(machine.mode.canned_cycles_return, GCodeCannedCycleReturnToR):
|
if isinstance(machine.mode.canned_cycles_return, GCodeCannedCycleReturnToR):
|
||||||
# canned return is to this.R, not this.Z (plane dependent)
|
# canned return is to this.R, not this.Z (plane dependent)
|
||||||
moveto_coords.update({
|
moveto_coords.update({
|
||||||
machine.mode.plane_selection.normal_axis: this.R,
|
machine.mode.plane_selection.normal_axis: self.R,
|
||||||
})
|
})
|
||||||
|
else: # default: GCodeCannedCycleReturnPrevLevel
|
||||||
|
# Remove this.Z (plane dependent) value (ie: no machine movement on this axis)
|
||||||
|
moveto_coords.pop(machine.mode.plane_selection.normal_axis, None)
|
||||||
|
|
||||||
machine.move_to(**moveto_coords)
|
# Process action 'L' times
|
||||||
|
loop_count = self.L
|
||||||
|
if (loop_count is None) or (loop_count <= 0):
|
||||||
|
loop_count = 1
|
||||||
|
for i in range(loop_count):
|
||||||
|
machine.move_to(**moveto_coords)
|
||||||
|
|
||||||
|
|
||||||
class GCodeDrillingCycle(GCodeCannedCycle):
|
class GCodeDrillingCycle(GCodeCannedCycle):
|
||||||
@ -929,7 +951,7 @@ class GCodeCannedReturnMode(GCode):
|
|||||||
exec_order = 220
|
exec_order = 220
|
||||||
|
|
||||||
|
|
||||||
class GCodeCannedCycleReturnLevel(GCodeCannedReturnMode):
|
class GCodeCannedCycleReturnPrevLevel(GCodeCannedReturnMode):
|
||||||
"""G98: Canned Cycle Return to the level set prior to cycle start"""
|
"""G98: Canned Cycle Return to the level set prior to cycle start"""
|
||||||
# "retract to the position that axis was in just before this series of one or more contiguous canned cycles was started"
|
# "retract to the position that axis was in just before this series of one or more contiguous canned cycles was started"
|
||||||
word_key = Word('G', 98)
|
word_key = Word('G', 98)
|
||||||
|
@ -8,6 +8,11 @@ add_pygcode_to_path()
|
|||||||
from pygcode.machine import Position, Machine
|
from pygcode.machine import Position, Machine
|
||||||
from pygcode.line import Line
|
from pygcode.line import Line
|
||||||
from pygcode.exceptions import MachineInvalidAxis
|
from pygcode.exceptions import MachineInvalidAxis
|
||||||
|
from pygcode.gcodes import (
|
||||||
|
GCodeAbsoluteDistanceMode, GCodeIncrementalDistanceMode,
|
||||||
|
GCodeAbsoluteArcDistanceMode, GCodeIncrementalArcDistanceMode,
|
||||||
|
GCodeCannedCycleReturnPrevLevel, GCodeCannedCycleReturnToR,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PositionTests(unittest.TestCase):
|
class PositionTests(unittest.TestCase):
|
||||||
@ -81,43 +86,146 @@ class PositionTests(unittest.TestCase):
|
|||||||
self.assertEqual(p / 2, Position(axes='XYZ', X=1, Y=5))
|
self.assertEqual(p / 2, Position(axes='XYZ', X=1, Y=5))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MachineGCodeProcessingTests(unittest.TestCase):
|
class MachineGCodeProcessingTests(unittest.TestCase):
|
||||||
def test_linear_movement(self):
|
def assert_processed_lines(self, line_data, machine):
|
||||||
m = Machine()
|
"""
|
||||||
test_str = '''; move in a 10mm square
|
Process lines & assert machine's position
|
||||||
F100 M3 S1000 ; 0
|
:param line_data: list of tuples [('g1 x2', {'X':2}), ... ]
|
||||||
g1 x0 y10 ; 1
|
"""
|
||||||
g1 x10 y10 ; 2
|
for (i, (line_str, expected_pos)) in enumerate(line_data):
|
||||||
g1 x10 y0 ; 3
|
line = Line(line_str)
|
||||||
g1 x0 y0 ; 4
|
|
||||||
'''
|
|
||||||
expected_pos = {
|
|
||||||
'0': m.Position(),
|
|
||||||
'1': m.Position(X=0, Y=10),
|
|
||||||
'2': m.Position(X=10, Y=10),
|
|
||||||
'3': m.Position(X=10, Y=0),
|
|
||||||
'4': m.Position(X=0, Y=0),
|
|
||||||
}
|
|
||||||
#print("\n%r\n%r" % (m.mode, m.state))
|
|
||||||
for line_text in str_lines(test_str):
|
|
||||||
line = Line(line_text)
|
|
||||||
if line.block:
|
if line.block:
|
||||||
#print("\n%s" % line.block)
|
machine.process_block(line.block)
|
||||||
m.process_block(line.block)
|
# Assert possition change correct
|
||||||
# Assert possition change correct
|
if expected_pos is not None:
|
||||||
comment = line.comment.text
|
p1 = machine.pos
|
||||||
if comment in expected_pos:
|
p2 = machine.Position(**expected_pos)
|
||||||
self.assertEqual(m.pos, expected_pos[comment])
|
self.assertEqual(p1, p2, "index:%i '%s': %r != %r" % (i, line_str, p1, p2))
|
||||||
#print("%r\n%r\npos=%r" % (m.mode, m.state, m.pos))
|
|
||||||
|
|
||||||
|
# Rapid Movement
|
||||||
|
def test_rapid_abs(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(GCodeAbsoluteDistanceMode())
|
||||||
|
line_data = [
|
||||||
|
('', {}), # start @ 0,0,0
|
||||||
|
('g0 x0 y10', {'X':0, 'Y':10}),
|
||||||
|
(' x10 y10', {'X':10, 'Y':10}),
|
||||||
|
(' x10 y0', {'X':10, 'Y':0}),
|
||||||
|
(' x0 y0', {'X':0, 'Y':0}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
#m = Machine()
|
def test_rapid_inc(self):
|
||||||
#
|
m = Machine()
|
||||||
#file = GCodeParser('part1.gcode')
|
m.process_gcodes(GCodeIncrementalDistanceMode())
|
||||||
#for line in file.iterlines():
|
line_data = [
|
||||||
# for (i, gcode) in enumerate(line.block.gcode):
|
('', {}), # start @ 0,0,0
|
||||||
# if isinstance(gcode, GCodeArcMove):
|
('g0 y10', {'X':0, 'Y':10}),
|
||||||
# arc = gcode
|
(' x10', {'X':10, 'Y':10}),
|
||||||
# line_params = arc.line_segments(precision=0.0005)
|
(' y-10', {'X':10, 'Y':0}),
|
||||||
# for
|
(' x-10', {'X':0, 'Y':0}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
|
# Linearly Interpolated Movement
|
||||||
|
def test_linear_abs(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(GCodeAbsoluteDistanceMode())
|
||||||
|
line_data = [
|
||||||
|
('g1 x0 y10', {'X':0, 'Y':10}),
|
||||||
|
(' x10 y10', {'X':10, 'Y':10}),
|
||||||
|
(' x10 y0', {'X':10, 'Y':0}),
|
||||||
|
(' x0 y0', {'X':0, 'Y':0}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
|
def test_linear_inc(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(GCodeIncrementalDistanceMode())
|
||||||
|
line_data = [
|
||||||
|
('g1 y10', {'X':0, 'Y':10}),
|
||||||
|
(' x10', {'X':10, 'Y':10}),
|
||||||
|
(' y-10', {'X':10, 'Y':0}),
|
||||||
|
(' x-10', {'X':0, 'Y':0}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
|
# Arc Movement
|
||||||
|
def test_arc_abs(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(
|
||||||
|
GCodeAbsoluteDistanceMode(),
|
||||||
|
GCodeIncrementalArcDistanceMode(),
|
||||||
|
)
|
||||||
|
line_data = [
|
||||||
|
# Clockwise circle in 4 segments
|
||||||
|
('g2 x0 y10 i5 j5', {'X':0, 'Y':10}),
|
||||||
|
(' x10 y10 i5 j-5', {'X':10, 'Y':10}),
|
||||||
|
(' x10 y0 i-5 j-5', {'X':10, 'Y':0}),
|
||||||
|
(' x0 y0 i-5 j5', {'X':0, 'Y':0}),
|
||||||
|
# Counter-clockwise circle in 4 segments
|
||||||
|
('g3 x10 y0 i5 j5', {'X':10, 'Y':0}),
|
||||||
|
(' x10 y10 i-5 j5', {'X':10, 'Y':10}),
|
||||||
|
(' x0 y10 i-5 j-5', {'X':0, 'Y':10}),
|
||||||
|
(' x0 y0 i5 j-5', {'X':0, 'Y':0}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
|
def test_arc_inc(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(
|
||||||
|
GCodeIncrementalDistanceMode(),
|
||||||
|
GCodeIncrementalArcDistanceMode(),
|
||||||
|
)
|
||||||
|
line_data = [
|
||||||
|
# Clockwise circle in 4 segments
|
||||||
|
('g2 y10 i5 j5', {'X':0, 'Y':10}),
|
||||||
|
(' x10 i5 j-5', {'X':10, 'Y':10}),
|
||||||
|
(' y-10 i-5 j-5', {'X':10, 'Y':0}),
|
||||||
|
(' x-10 i-5 j5', {'X':0, 'Y':0}),
|
||||||
|
# Counter-clockwise circle in 4 segments
|
||||||
|
('g3 x10 i5 j5', {'X':10, 'Y':0}),
|
||||||
|
(' y10 i-5 j5', {'X':10, 'Y':10}),
|
||||||
|
(' x-10 i-5 j-5', {'X':0, 'Y':10}),
|
||||||
|
(' y-10 i5 j-5', {'X':0, 'Y':0}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
|
# Canned Drilling Cycles
|
||||||
|
def test_canned_return2oldz(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(
|
||||||
|
GCodeAbsoluteDistanceMode(),
|
||||||
|
GCodeCannedCycleReturnPrevLevel(),
|
||||||
|
)
|
||||||
|
line_data = [
|
||||||
|
('g0 z5', {'Z':5}),
|
||||||
|
('g81 x10 y20 z-2 r1', {'X':10, 'Y':20, 'Z':5}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
|
def test_canned_return2r(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(
|
||||||
|
GCodeAbsoluteDistanceMode(),
|
||||||
|
GCodeCannedCycleReturnToR(),
|
||||||
|
)
|
||||||
|
line_data = [
|
||||||
|
('g0 z5', {'Z':5}),
|
||||||
|
('g81 x10 y20 z-2 r1', {'X':10, 'Y':20, 'Z':1}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
|
||||||
|
def test_canned_loops(self):
|
||||||
|
m = Machine()
|
||||||
|
m.process_gcodes(
|
||||||
|
GCodeAbsoluteDistanceMode(),
|
||||||
|
GCodeCannedCycleReturnPrevLevel(),
|
||||||
|
)
|
||||||
|
line_data = [
|
||||||
|
('g0 z5', None),
|
||||||
|
('g81 x10 y20 z-2 r1 l2', {'X':10, 'Y':20, 'Z':5}),
|
||||||
|
('g91', None), # switch to incremental mode
|
||||||
|
('g81 x10 y20 z-2 r1 l2', {'X':30, 'Y':60, 'Z':5}),
|
||||||
|
]
|
||||||
|
self.assert_processed_lines(line_data, m)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user