Commit 753bcce0 authored by Lars Kruse's avatar Lars Kruse

added new GCode exporters (generic + LinuxCNC)

The file LinucCNC.py is supposed to be a good example for a machine-specific
language export definition.
parent a5e7a4a8
# -*- coding: utf-8 -*-
"""
Copyright 2012 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import pycam.Exporters.GCode
from pycam.Toolpath import CORNER_STYLE_EXACT_PATH, CORNER_STYLE_EXACT_STOP, \
CORNER_STYLE_OPTIMIZE_SPEED, CORNER_STYLE_OPTIMIZE_TOLERANCE
DEFAULT_HEADER = (("G40", "disable tool radius compensation"),
("G49", "disable tool length compensation"),
("G80", "cancel modal motion"),
("G54", "select coordinate system 1"),
("G90", "disable incremental moves"))
DEFAULT_DIGITS = 6
def _render_number(number):
if int(number) == number:
return "%d" % number
else:
return ("%%.%df" % DEFAULT_DIGITS) % number
class LinuxCNC(pycam.Exporters.GCode.BaseGenerator):
def add_header(self):
for command, comment in DEFAULT_HEADER:
self.add_command(command, comment=comment)
# TODO: use a "unit" filter
if True:
self.add_command("G21", "metric")
else:
self.add_command("G20", "imperial")
def add_footer(self):
self.add_command("M2", "end program")
def add_comment(self, comment):
self.add_command("; %s" % comment)
def add_command(self, command, comment=None):
self.destination.write(command)
if comment:
self.destination.write("\t")
self.add_comment(comment)
else:
self.destination.write(os.linesep)
def add_move(self, coordinates, is_rapid=False):
components = []
# the cached value may be:
# True: the last move was G0
# False: the last move was G1
# None: some non-move happened before
if self._get_cache("rapid_move", None) != is_rapid:
components.append("G0" if is_rapid else "G1")
else:
# improve gcode style
components.append(" ")
axes = [axis for axis in "XYZABCUVW"]
previous = self._get_cache("position", [None] * len(coordinates))
for (axis, value, last) in zip(axes, coordinates, previous):
if (last is None) or (last != value):
components.append("%s%.6f" % (axis, value))
command = " ".join(components)
if command.strip():
self.add_command(command)
def command_feedrate(self, feedrate):
self.add_command("F%s" % _render_number(feedrate), "set feedrate")
def command_select_tool(self, tool_id):
self.add_command("T%d M6" % tool_id, "select tool")
def command_spindle_speed(self, speed):
self.add_command("S%s" % _render_number(speed), "set spindle speed")
def command_spindle_enabled(self, state):
if state:
self.add_command("M3", "start spindle")
else:
self.add_command("M5", "stop spindle")
def command_delay(self, seconds):
self.add_command("G04 P%d" % seconds, "wait for %d seconds" % seconds)
def command_corner_style(self,
(path_mode, motion_tolerance, naive_tolerance)):
if path_mode == CORNER_STYLE_EXACT_PATH:
self.add_command("G61", "exact path mode")
elif path_mode == CORNER_STYLE_EXACT_STOP:
self.add_command("G61.1", "exact stop mode")
elif path_mode == CORNER_STYLE_OPTIMIZE_SPEED:
self.add_command("G64", "continuous mode with maximum speed")
else:
if not naive_tolerance:
self.add_command("G64 P%f" % motion_tolerance,
"continuous mode with tolerance")
else:
self.add_command("G64 P%f Q%f" % \
(motion_tolerance, naive_tolerance),
"continuous mode with tolerance and cleanup")
# -*- coding: utf-8 -*-
"""
Copyright 2012 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import decimal
import os
import pycam.Utils.log
import pycam.Toolpath.Filters
from pycam.Toolpath import MOVE_STRAIGHT, MOVE_STRAIGHT_RAPID, \
MACHINE_SETTING, COMMENT
_log = pycam.Utils.log.get_logger()
class BaseGenerator(object):
def __init__(self, destination):
if isinstance(destination, basestring):
# open the file
self.destination = file(destination,"w")
self._close_stream_on_exit = True
else:
# assume that "destination" is something like a StringIO instance
# or an open file
self.destination = destination
# don't close the stream if we did not open it on our own
self._close_stream_on_exit = False
self._filters = []
self._cache = {}
self.add_header()
def _get_cache(self, key, default_value):
return self._cache.get(key, default_value)
def add_filters(self, filters):
self._filters.extend(filters)
self._filters.sort()
def add_comment(self, comment):
raise NotImplementedError("someone forgot to implement 'add_comment'")
def add_command(self, command, comment=None):
raise NotImplementedError("someone forgot to implement 'add_command'")
def add_move(self, coordinates, is_rapid=False):
raise NotImplementedError("someone forgot to implement 'add_move'")
def add_footer(self):
raise NotImplementedError("someone forgot to implement 'add_footer'")
def finish(self):
self.add_footer()
if self._close_stream_on_exit:
self.destination.close()
def add_moves(self, moves, filters=None):
# combine both lists/tuples in a type-agnostic way
all_filters = list(self._filters)
if filters:
all_filters.extend(filters)
filtered_moves = pycam.Toolpath.Filters.get_filtered_moves(moves,
all_filters)
for move_type, args in filtered_moves:
if move_type in (MOVE_STRAIGHT, MOVE_STRAIGHT_RAPID):
is_rapid = move_type == MOVE_STRAIGHT_RAPID
self.add_move(args, is_rapid)
self._cache["position"] = args
self._cache["rapid_move"] = is_rapid
elif move_type == COMMENT:
self.add_comment(args)
elif move_type == MACHINE_SETTING:
key, value = args
func_name = "command_%s" % key
if hasattr(self, func_name):
_log.debug("GCode: machine setting '%s': %s" % (key, value))
getattr(self, func_name)(value)
self._cache[key] = value
self._cache["rapid_move"] = None
else:
_log.warn("The current GCode exporter does not support " + \
"the machine setting '%s=%s' -> ignore" % (key, value))
else:
_log.warn(("A non-basic toolpath item (%d -> %s) remained in the " + \
"queue -> ignore") % (move_type, args))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment