diff --git a/mecode/devices/efd_pico_pulse.py b/mecode/devices/efd_pico_pulse.py index b52c0a3..a37a138 100644 --- a/mecode/devices/efd_pico_pulse.py +++ b/mecode/devices/efd_pico_pulse.py @@ -29,29 +29,29 @@ def disconnect(self): self.s.close() def send(self, command): - """Send message over serial to PicoTouch controller.""" + """Send message over serial to PicoTouch controller.""" msg = command + EOT self.s.write(msg) # return response and remove ACK return self.s.read_until(ACK)[:-2] def set_valve_mode(self, mode): - """Set valve mode to Timed, Purge, Continous, or read current mode. + """Set valve mode to Timed, Purge, Continous, or read current mode. - Keyword argument: - mode -- 1 = Timed; 2 = Purge; 3 = Continuous; 5 = read current mode """ + Keyword argument: + mode -- 1 = Timed; 2 = Purge; 3 = Continuous; 5 = read current mode """ return self.send(str(mode) + 'drv1') def set_dispense_count(self, count): - """Set how many times valve dispenses with each cycle.""" + """Set how many times valve dispenses with each cycle.""" return self.send('{:05}'.format(count) + 'dcn1') def get_valve_status(self): - """Return valve's current parameters and dispense statistics.""" + """Return valve's current parameters and dispense statistics.""" return self.send('rdr1') def cycle_valve(self): - """Cycle the valve (eqiuvalent to pressing cycle button).""" + """Cycle the valve (eqiuvalent to pressing cycle button).""" return self.send('1cycl') + self.send('0cycl') def set_heater_mode(self, mode): @@ -66,18 +66,18 @@ def set_heater_temp(self, temp): return self.send('{:05.1f}'.format(temp) + 'stmp') def get_heater_status(self): - """Return mode, heater setpoint temp, and heater actual temp.""" + """Return mode, heater setpoint temp, and heater actual temp.""" return self.send('rhtr') def get_valve_info(self): - """Return controller and valve SN and type, fw version, pcb rev.""" + """Return controller and valve SN and type, fw version, pcb rev.""" return self.send('info') def get_alarm_hist(self): - """Return last 40 alarm conditions with time and alarm name.""" + """Return last 40 alarm conditions with time and alarm name.""" return self.send('ralr') def reset_alarm(self): - """Reset a currently active alarm.""" + """Reset a currently active alarm.""" return self.send('arst') diff --git a/mecode/main.py b/mecode/main.py index 89adb9f..2dceebb 100644 --- a/mecode/main.py +++ b/mecode/main.py @@ -110,7 +110,8 @@ def __init__(self, outfile=None, print_lines='auto', header=None, footer=None, extrusion_multiplier=1, setup=True, lineend='os', - comment_char=';'): + comment_char=';', + absolute=False): """ Parameters ---------- @@ -179,6 +180,11 @@ def __init__(self, outfile=None, print_lines='auto', header=None, footer=None, lineending insertion. comment_char : str (default: ';') Character to use when outputting comments. + Special case handling for parenthesis comments. If "(" is specified + as the comment symbol, the comments will be wrapped in both opening + and closing parenthesis: `G1 X5 ( this is a comment )` + absolute : bool (default: False) + Should the system default to relative or absolute mode """ self.outfile = outfile @@ -199,7 +205,7 @@ def __init__(self, outfile=None, print_lines='auto', header=None, footer=None, self.i_axis = i_axis self.j_axis = j_axis self.k_axis = k_axis - self.comment_char = comment_char + self._comment_char = comment_char self._current_position = defaultdict(float) self.is_relative = True @@ -237,6 +243,10 @@ def __init__(self, outfile=None, print_lines='auto', header=None, footer=None, if setup: self.setup() + if absolute: + self.absolute() + + @property def current_position(self): return self._current_position @@ -259,6 +269,54 @@ def __exit__(self, exc_type, exc_value, traceback): """ self.teardown() + def _commentify(self, txt): + ''' + Format text `txt` in whichever manner is needed to indicate it's a comment. + The mode is set by the `comment_char` parameter on class construction + ''' + + # We need to special case using parenthesis for comments, since we have to + # wrap them around the beginning and the end of the comment + if self._comment_char == "(": + return "( {} )".format(txt) + + # Otherwise, just prepend the comment character (and a space) + return "{} {}".format(self._comment_char, txt) + + + def write_comment(self, comment): + """ Insert a comment into the gcode output file + """ + self.write(self._commentify(comment)) + + def move_other_axis(self, a, rapid=False): + """ Move an auxilliary axis (currently "A") + + Primarily, this is useful for a rotational indexer (if present) + """ + + cmd = 'G0 ' if rapid else 'G1 ' + self.write(cmd + "A{:.{digits}f}".format(a, digits=self.output_digits)) + + def insert_machine_stop(self, comment=None): + '''Insert a machine stop (M00) command with an optional comment + ''' + if comment: + self.write("M00 {}".format(self._commentify(comment))) + else: + self.write("M00") + + def insert_optional_stop(self, comment=None): + '''Insert a optional stop (M01) command with an optional comment + ''' + if comment: + self.write("M01 {}".format(self._commentify(comment))) + else: + self.write("M01") + + + + # GCode Aliases ######################################################## def set_home(self, x=None, y=None, z=None, **kwargs): @@ -272,7 +330,7 @@ def set_home(self, x=None, y=None, z=None, **kwargs): """ args = self._format_args(x, y, z, **kwargs) space = ' ' if len(args) > 0 else '' - self.write('G92' + space + args + ' {}set home'.format(self.comment_char)) + self.write('G92' + space + args + " " +self._commentify('set home')) self._update_current_position(mode='absolute', x=x, y=y, z=z, **kwargs) @@ -282,7 +340,7 @@ def reset_home(self): # FIXME This does not work with internal current_position # FIXME You must call an abs_move after this to re-sync # current_position - self.write('G92.1 {}reset position to machine coordinates without moving'.format(self.comment_char)) + self.write('G92.1 {}'.format(self._commentify("reset position to machine coordinates without moving"))) def relative(self): """ Enter relative movement mode, in general this method should not be @@ -290,7 +348,7 @@ def relative(self): """ if not self.is_relative: - self.write('G91 {}relative'.format(self.comment_char)) + self.write('G91 {}'.format(self._commentify("relative"))) self.is_relative = True def absolute(self): @@ -299,7 +357,7 @@ def absolute(self): """ if self.is_relative: - self.write('G90 {}absolute'.format(self.comment_char)) + self.write('G90 {}'.format(self._commentify("absolute"))) self.is_relative = False def feed(self, rate): @@ -314,7 +372,7 @@ def feed(self, rate): self.write('G1 F{}'.format(rate)) self.speed = rate - def dwell(self, time): + def dwell(self, time, comment=None): """ Pause code executions for the given amount of time. Parameters @@ -323,7 +381,11 @@ def dwell(self, time): Time in milliseconds to pause code execution. """ - self.write('G4 P{}'.format(time)) + + if comment: + self.write('G4 P{} {}'.format(time, self._commentify(comment))) + else: + self.write('G4 P{}'.format(time)) # Composed Functions ##################################################### @@ -334,9 +396,9 @@ def setup(self): """ self._write_header() if self.is_relative: - self.write('G91 {}relative'.format(self.comment_char)) + self.write('G91 {}'.format(self._commentify("relative"))) else: - self.write('G90 {}absolute'.format(self.comment_char)) + self.write('G90 {}'.format(self._commentify("absolute"))) def teardown(self, wait=True): """ Close the outfile file after writing the footer if opened. This @@ -482,14 +544,14 @@ def arc(self, x=None, y=None, z=None, direction='CW', radius='auto', raise RuntimeError(msg) dimensions = [k.lower() for k in dims.keys()] if 'x' in dimensions and 'y' in dimensions: - plane_selector = 'G17 {}XY plane'.format(self.comment_char) # XY plane + plane_selector = 'G17 {}'.format(self._commentify('XY plane')) # XY plane axis = helix_dim elif 'x' in dimensions: - plane_selector = 'G18 {}XZ plane'.format(self.comment_char) # XZ plane + plane_selector = 'G18 {}'.format(self._commentify('XZ plane')) # XZ plane dimensions.remove('x') axis = dimensions[0].upper() elif 'y' in dimensions: - plane_selector = 'G19 {}YZ plane'.format(self.comment_char) # YZ plane + plane_selector = 'G19 {}'.format(self._commentify('YZ plane')) # YZ plane dimensions.remove('y') axis = dimensions[0].upper() else: @@ -539,7 +601,7 @@ def arc(self, x=None, y=None, z=None, direction='CW', radius='auto', dims['E'] = filament_length + current_extruder_position if axis is not None: - self.write('G16 X Y {} {}coordinate axis assignment'.format(axis, self.comment_char)) # coordinate axis assignment + self.write('G16 X Y {} {}'.format(axis, self._commentify("coordinate axis assignment"))) # coordinate axis assignment self.write(plane_selector) args = self._format_args(**dims) if helix_dim is None: @@ -582,7 +644,7 @@ def arc_ijk(self, target, center, plane, direction='CW', helix_len=None): raise RuntimeError("'center' must be a 2-tuple of numbers (passed %s)" % center) if plane == 'xy': - self.write('G17 {}XY plane'.format(self.comment_char)) # XY plane + self.write('G17 {}'.format(self._commentify('XY plane'))) # XY plane dims = { 'x' : target[0], 'y' : target[1], @@ -592,7 +654,7 @@ def arc_ijk(self, target, center, plane, direction='CW', helix_len=None): if helix_len: dims['z'] = helix_len elif plane == 'yz': - self.write('G19 {}YZ plane'.format(self.comment_char)) # YZ plane + self.write('G19 {}'.format(self._commentify('YZ plane'))) # YZ plane dims = { 'y' : target[0], 'z' : target[1], @@ -602,7 +664,7 @@ def arc_ijk(self, target, center, plane, direction='CW', helix_len=None): if helix_len: dims['x'] = helix_len elif plane == 'xz': - self.write('G18 {}XZ plane'.format(self.comment_char)) # XZ plane + self.write('G18 {}'.format(self._commentify('XZ plane'))) # XZ plane dims = { 'x' : target[0], 'z' : target[1], @@ -756,8 +818,7 @@ def meander(self, x, y, spacing, start='LL', orientation='x', tail=False, actual_spacing = self._meander_spacing(minor, spacing) if abs(actual_spacing) != spacing: - msg = '{}WARNING! meander spacing updated from {} to {}' - self.write(msg.format(self.comment_char, spacing, actual_spacing)) + self.write(self._commentify("WARNING! meander spacing updated from {} to {}".format(spacing, actual_spacing))) spacing = actual_spacing sign = 1 diff --git a/mecode/tests/test_main.py b/mecode/tests/test_main.py index 69cc094..e2bc312 100755 --- a/mecode/tests/test_main.py +++ b/mecode/tests/test_main.py @@ -25,9 +25,9 @@ def setUp(self): aerotech_include=False) self.expected = "" if self.g.is_relative: - self.expect_cmd('G91 ;relative') + self.expect_cmd('G91 ; relative') else: - self.expect_cmd('G90 ;absolute') + self.expect_cmd('G90 ; absolute') def tearDown(self): self.g.teardown() @@ -68,10 +68,10 @@ def test_init(self): def test_set_home(self): g = self.g g.set_home() - self.expect_cmd('G92 ;set home') + self.expect_cmd('G92 ; set home') self.assert_output() g.set_home(x=10, y=20, A=5) - self.expect_cmd('G92 X10.000000 Y20.000000 A5.000000 ;set home') + self.expect_cmd('G92 X10.000000 Y20.000000 A5.000000 ; set home') self.assert_output() self.assert_position({'A': 5.0, 'x': 10.0, 'y': 20.0, 'z': 0}) g.set_home(y=0) @@ -79,16 +79,16 @@ def test_set_home(self): def test_reset_home(self): self.g.reset_home() - self.expect_cmd('G92.1 ;reset position to machine coordinates without moving') + self.expect_cmd('G92.1 ; reset position to machine coordinates without moving') self.assert_output() def test_relative(self): self.assertEqual(self.g.is_relative, True) self.g.absolute() - self.expect_cmd('G90 ;absolute') + self.expect_cmd('G90 ; absolute') self.g.relative() self.assertEqual(self.g.is_relative, True) - self.expect_cmd('G91 ;relative') + self.expect_cmd('G91 ; relative') self.assert_output() self.g.relative() self.assertEqual(self.g.is_relative, True) @@ -97,7 +97,7 @@ def test_relative(self): def test_absolute(self): self.g.absolute() self.assertEqual(self.g.is_relative, False) - self.expect_cmd('G90 ;absolute') + self.expect_cmd('G90 ; absolute') self.assert_output() self.g.absolute() self.assertEqual(self.g.is_relative, False) @@ -121,15 +121,15 @@ def test_setup(self): with open(os.path.join(HERE, '../header.txt')) as f: lines = f.read() self.expect_cmd(lines) - self.expect_cmd('G91 ;relative') + self.expect_cmd('G91 ; relative') self.assert_output() def test_home(self): self.g.home() self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X0.000000 Y0.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 0, 'y': 0, 'z': 0}) @@ -150,9 +150,9 @@ def test_move(self): self.g.abs_move(20, 20, 0) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X20.000000 Y20.000000 Z0.000000 - G91 ;relative + G91 ; relative """) self.assert_output() @@ -166,9 +166,9 @@ def test_move(self): self.assert_position({'x': 30.0, 'y': 30.0, 'z': 0.0, 'A': 50.0, 'E': 0.45635101227893116}) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X30.000000 Y30.000000 E0.456351 - G91 ;relative + G91 ; relative """) self.assert_output() @@ -202,9 +202,9 @@ def test_move(self): self.assert_position({'x': 40.0, 'y': 40.0, 'Z': 20, 'A':50, 'z':0.0, 'E': 1.4244176984302641}) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 E1.424418 Z20.000000 - G91 ;relative + G91 ; relative """) self.assert_output() @@ -221,27 +221,27 @@ def test_abs_move(self): self.g.relative() self.g.abs_move(10, 10) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X10.000000 Y10.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 10, 'y': 10, 'z': 0}) self.g.abs_move(5, 5, 5) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X5.000000 Y5.000000 Z5.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 5, 'y': 5, 'z': 5}) self.g.abs_move(15, 0, D=5) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X15.000000 Y0.000000 D5.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 15, 'y': 0, 'D': 5, 'z': 5}) @@ -249,7 +249,7 @@ def test_abs_move(self): self.g.absolute() self.g.abs_move(19, 18, D=6) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X19.000000 Y18.000000 D6.000000 """) self.assert_output() @@ -272,9 +272,9 @@ def test_rapid(self): self.g.abs_rapid(20, 20, 0) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G0 X20.000000 Y20.000000 Z0.000000 - G91 ;relative + G91 ; relative """) self.assert_output() @@ -289,27 +289,27 @@ def test_abs_rapid(self): self.g.relative() self.g.abs_rapid(10, 10) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G0 X10.000000 Y10.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 10, 'y': 10, 'z': 0}) self.g.abs_rapid(5, 5, 5) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G0 X5.000000 Y5.000000 Z5.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 5, 'y': 5, 'z': 5}) self.g.abs_rapid(15, 0, D=5) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G0 X15.000000 Y0.000000 D5.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 15, 'y': 0, 'D': 5, 'z': 5}) @@ -317,7 +317,7 @@ def test_abs_rapid(self): self.g.absolute() self.g.abs_rapid(19, 18, D=6) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G0 X19.000000 Y18.000000 D6.000000 """) self.assert_output() @@ -330,7 +330,7 @@ def test_arc(self): self.g.arc(x=10, y=0) self.expect_cmd(""" - G17 ;XY plane + G17 ; XY plane G2 X10.000000 Y0.000000 R5.000000 """) self.assert_output() @@ -338,8 +338,8 @@ def test_arc(self): self.g.arc(x=5, A=0, direction='CCW', radius=5) self.expect_cmd(""" - G16 X Y A ;coordinate axis assignment - G18 ;XZ plane + G16 X Y A ; coordinate axis assignment + G18 ; XZ plane G3 X5.000000 A0.000000 R5.000000 """) self.assert_output() @@ -347,8 +347,8 @@ def test_arc(self): self.g.arc(x=0, y=10, helix_dim='D', helix_len=10) self.expect_cmd(""" - G16 X Y D ;coordinate axis assignment - G17 ;XY plane + G16 X Y D ; coordinate axis assignment + G17 ; XY plane G2 X0.000000 Y10.000000 R5.000000 G1 D10 """) self.assert_output() @@ -356,8 +356,8 @@ def test_arc(self): self.g.arc(0, 10, helix_dim='D', helix_len=10) self.expect_cmd(""" - G16 X Y D ;coordinate axis assignment - G17 ;XY plane + G16 X Y D ; coordinate axis assignment + G17 ; XY plane G2 X0.000000 Y10.000000 R5.000000 G1 D10 """) self.assert_output() @@ -370,20 +370,20 @@ def test_abs_arc(self): self.g.relative() self.g.abs_arc(x=0, y=10) self.expect_cmd(""" - G90 ;absolute - G17 ;XY plane + G90 ; absolute + G17 ; XY plane G2 X0.000000 Y10.000000 R5.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 0, 'y': 10, 'z': 0}) self.g.abs_arc(x=0, y=10) self.expect_cmd(""" - G90 ;absolute - G17 ;XY plane + G90 ; absolute + G17 ; XY plane G2 X0.000000 Y10.000000 R0.000000 - G91 ;relative + G91 ; relative """) self.assert_output() self.assert_position({'x': 0, 'y': 10, 'z': 0}) @@ -391,8 +391,8 @@ def test_abs_arc(self): self.g.absolute() self.g.abs_arc(x=0, y=20) self.expect_cmd(""" - G90 ;absolute - G17 ;XY plane + G90 ; absolute + G17 ; XY plane G2 X0.000000 Y20.000000 R5.000000 """) self.assert_output() @@ -494,7 +494,7 @@ def test_meander(self): self.g.meander(2, 2, 1.1) self.expect_cmd(""" - ;WARNING! meander spacing updated from 1.1 to 1.0 + ; WARNING! meander spacing updated from 1.1 to 1.0 G1 X2.000000 G1 Y1.000000 G1 X-2.000000 @@ -552,8 +552,8 @@ def test_meander(self): self.g.absolute() self.g.meander(3, 2, 1, start='LR', orientation='y') self.expect_cmd(""" - G90 ;absolute - G91 ;relative + G90 ; absolute + G91 ; relative G1 Y2.000000 G1 X-1.000000 G1 Y-2.000000 @@ -561,7 +561,7 @@ def test_meander(self): G1 Y2.000000 G1 X-1.000000 G1 Y-2.000000 - G90 ;absolute + G90 ; absolute """) self.assert_output() self.assert_position({'x': -3, 'y': 4, 'z': 0}) @@ -569,8 +569,8 @@ def test_meander(self): def test_clip(self): self.g.clip() self.expect_cmd(""" - G16 X Y Z ;coordinate axis assignment - G18 ;XZ plane + G16 X Y Z ; coordinate axis assignment + G18 ; XZ plane G3 X0.000000 Z4.000000 R2.000000 """) self.assert_output() @@ -578,8 +578,8 @@ def test_clip(self): self.g.clip(axis='A', direction='-y', height=10) self.expect_cmd(""" - G16 X Y A ;coordinate axis assignment - G19 ;YZ plane + G16 X Y A ; coordinate axis assignment + G19 ; YZ plane G2 Y0.000000 A10.000000 R5.000000 """) self.assert_output() @@ -587,8 +587,8 @@ def test_clip(self): self.g.clip(axis='A', direction='-y', height=-10) self.expect_cmd(""" - G16 X Y A ;coordinate axis assignment - G19 ;YZ plane + G16 X Y A ; coordinate axis assignment + G19 ; YZ plane G3 Y0.000000 A-10.000000 R5.000000 """) self.assert_output() @@ -640,8 +640,8 @@ def test_rename_axis(self): self.assert_position({'x': 40.0, 'y': 30.0, 'z': 40, 'A': 10, 'B': 30, 'W': 10}) self.expect_cmd(""" - G16 X Y B ;coordinate axis assignment - G18 ;XZ plane + G16 X Y B ; coordinate axis assignment + G18 ; XZ plane G2 X10.000000 B10.000000 R7.071068 """) self.assert_output() @@ -650,11 +650,11 @@ def test_rename_axis(self): self.assert_position({'x': 0.0, 'y': 30.0, 'z': 0, 'A': 10, 'B': 0, 'W': 10}) self.expect_cmd(""" - G90 ;absolute - G16 X Y B ;coordinate axis assignment - G18 ;XZ plane + G90 ; absolute + G16 X Y B ; coordinate axis assignment + G18 ; XZ plane G2 X0.000000 B0.000000 R28.284271 - G91 ;relative + G91 ; relative """) self.assert_output() @@ -722,11 +722,11 @@ def test_triangular_wave(self): self.g.absolute() self.g.triangular_wave(3, 2, 1, start='LR', orientation='y') self.expect_cmd(""" - G90 ;absolute - G91 ;relative + G90 ; absolute + G91 ; relative G1 X3.000000 Y-2.000000 G1 X-3.000000 Y-2.000000 - G90 ;absolute + G90 ; absolute """) self.assert_output() self.assert_position({'x': 3, 'y': 4, 'z': 0}) diff --git a/mecode/tests/test_matrix.py b/mecode/tests/test_matrix.py index 85e9a02..52464d4 100755 --- a/mecode/tests/test_matrix.py +++ b/mecode/tests/test_matrix.py @@ -90,12 +90,12 @@ def test_abs_zmove_with_flip(self): self.assert_almost_position({'x': 1.0, 'y': 0, 'z': 2}) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X-1.000000 Y0.000000 Z0.000000 - G91 ;relative - G90 ;absolute + G91 ; relative + G90 ; absolute G1 X-1.000000 Y0.000000 Z2.000000 - G91 ;relative + G91 ; relative """) self.assert_output() @@ -106,12 +106,12 @@ def test_abs_zmove_with_rotate(self): self.g.abs_move(z=2) self.assert_almost_position({'x': 1.0, 'y': 0, 'z': 2}) self.expect_cmd(""" - G90 ;absolute + G90 ; absolute G1 X0.000000 Y1.000000 Z0.000000 - G91 ;relative - G90 ;absolute + G91 ; relative + G90 ; absolute G1 X0.000000 Y1.000000 Z2.000000 - G91 ;relative + G91 ; relative """) self.assert_output() @@ -128,7 +128,7 @@ def test_arc(self): self.g.rotate(math.pi/2) self.g.arc(x=10, y=0) self.expect_cmd(""" - G17 ;XY plane + G17 ; XY plane G2 X0.000000 Y10.000000 R5.000000 """) self.assert_output() @@ -139,7 +139,7 @@ def test_arc_reflect(self): self.g.arc(x=10,y=0) # Without the reflect this would be a G2. self.expect_cmd(""" - G17 ;XY plane + G17 ; XY plane G3 X10.000000 Y0.000000 R5.000000 """) self.assert_output()