kopia lustrzana https://github.com/vilemduha/blendercam
1879 wiersze
59 KiB
Python
1879 wiersze
59 KiB
Python
################################################################################
|
|
# iso.py
|
|
#
|
|
# Simple ISO NC code creator
|
|
#
|
|
# Hirutso Enni, 2009-01-13
|
|
|
|
from . import nc
|
|
import math
|
|
from .format import Format
|
|
from .format import *
|
|
import bpy
|
|
|
|
################################################################################
|
|
|
|
|
|
class Creator(nc.Creator):
|
|
def __init__(self):
|
|
nc.Creator.__init__(self)
|
|
|
|
# internal variables
|
|
self.a = 0
|
|
self.b = 0
|
|
self.c = 0
|
|
self.f = Address("F", fmt=Format(number_of_decimal_places=2))
|
|
self.fh = None
|
|
self.fv = None
|
|
self.fhv = False
|
|
self.g_plane = Address("G", fmt=Format(number_of_decimal_places=0))
|
|
self.g_list = []
|
|
self.i = 0
|
|
self.j = 0
|
|
self.k = 0
|
|
self.m = []
|
|
self.r = 0
|
|
self.s = AddressPlusMinus("S", fmt=Format(number_of_decimal_places=2), modal=False)
|
|
self.t = None
|
|
self.x = None
|
|
self.y = None
|
|
self.z = None
|
|
self.g0123_modal = False
|
|
self.drill_modal = False
|
|
self.prev_f = ""
|
|
self.prev_g0123 = ""
|
|
self.prev_drill = ""
|
|
self.prev_retract = ""
|
|
self.prev_z = ""
|
|
self.useCrc = False
|
|
self.useCrcCenterline = False
|
|
self.gCRC = ""
|
|
self.fmt = Format()
|
|
self.absolute_flag = True
|
|
self.ffmt = Format(number_of_decimal_places=2)
|
|
self.sfmt = Format(number_of_decimal_places=1)
|
|
self.in_quadrant_splitting = False
|
|
self.in_canned_cycle = False
|
|
self.first_drill_pos = True
|
|
self.shift_x = 0.0
|
|
self.shift_y = 0.0
|
|
self.shift_z = 0.0
|
|
self.start_of_line = False
|
|
self.internal_coolant_on = None
|
|
self.g98_not_g99 = None # True for G98 ouput, False for G99 output
|
|
self.current_fixture = None
|
|
self.fixture_wanted = "54"
|
|
self.move_done_since_tool_change = False
|
|
self.tool_defn_params = {}
|
|
self.program_id = None
|
|
self.current_sub_id = None
|
|
self.subroutine_files = []
|
|
self.program_name = None
|
|
self.temp_file_to_append_on_close = None
|
|
self.fixture_order = ["54", "55", "56", "57", "58", "59"]
|
|
for i in range(1, 50):
|
|
self.fixture_order.append("54." + str(i))
|
|
self.output_disabled = False
|
|
self.z_for_g43 = None
|
|
|
|
# optional settings
|
|
self.arc_centre_absolute = False
|
|
self.arc_centre_positive = False
|
|
self.drillExpanded = False
|
|
self.dwell_allowed_in_G83 = False
|
|
self.can_do_helical_arcs = True
|
|
self.z_for_g53 = (
|
|
None # set this to a value to output G53 Zvalue in tool change and at program end
|
|
)
|
|
self.output_h_and_d_at_tool_change = False
|
|
self.output_block_numbers = True
|
|
self.start_block_number = 10
|
|
self.block_number_increment = 10
|
|
self.block_number_restart_after = None
|
|
self.output_tool_definitions = True
|
|
self.output_tool_change = True
|
|
self.output_G43_on_tool_change_line = False
|
|
self.output_internal_coolant_commands = False
|
|
self.output_g98_and_g99 = True
|
|
self.output_g43_z_before_drilling_if_g98 = False
|
|
self.output_cutviewer_comments = False
|
|
self.output_fixtures = False
|
|
self.use_this_program_id = None
|
|
self.subroutines_in_own_files = False
|
|
self.pattern_done_with_subroutine = False
|
|
self.output_comment_before_tool_change = True
|
|
self.output_arcs_as_lines = False
|
|
self.m_codes_on_their_own_line = False
|
|
|
|
############################################################################
|
|
# Codes
|
|
|
|
def SPACE_STR(self):
|
|
return ""
|
|
|
|
def SPACE(self):
|
|
if self.start_of_line == True:
|
|
self.start_of_line = False
|
|
return ""
|
|
else:
|
|
return self.SPACE_STR()
|
|
|
|
def FORMAT_FEEDRATE(self):
|
|
return "%.2f"
|
|
|
|
def FORMAT_ANG(self):
|
|
return "%.1f"
|
|
|
|
def FORMAT_TIME(self):
|
|
return self.fmt
|
|
|
|
def BLOCK(self):
|
|
return "N%i"
|
|
|
|
def COMMENT(self, comment):
|
|
return "(%s)" % comment
|
|
|
|
def VARIABLE(self):
|
|
return "#%i"
|
|
|
|
def VARIABLE_SET(self):
|
|
return "=%.3f"
|
|
|
|
def PROGRAM(self):
|
|
return "O%i"
|
|
|
|
def PROGRAM_END(self):
|
|
return "M02"
|
|
|
|
def SUBPROG_CALL(self):
|
|
return "M98" + self.SPACE() + "P%i"
|
|
|
|
def SUBPROG_END(self):
|
|
return "M99"
|
|
|
|
def STOP_OPTIONAL(self):
|
|
return "M01"
|
|
|
|
def STOP(self):
|
|
return "M00"
|
|
|
|
def IMPERIAL(self):
|
|
return "G20"
|
|
|
|
def METRIC(self):
|
|
return "G21"
|
|
|
|
def ABSOLUTE(self):
|
|
return "G90"
|
|
|
|
def INCREMENTAL(self):
|
|
return "G91"
|
|
|
|
def SET_TEMPORARY_COORDINATE_SYSTEM(self):
|
|
return "G92"
|
|
|
|
def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self):
|
|
return "G92.1"
|
|
|
|
def POLAR_ON(self):
|
|
return "G16"
|
|
|
|
def POLAR_OFF(self):
|
|
return "G15"
|
|
|
|
def PLANE_XY(self):
|
|
return "17"
|
|
|
|
def PLANE_XZ(self):
|
|
return "18"
|
|
|
|
def PLANE_YZ(self):
|
|
return "19"
|
|
|
|
def TOOL(self):
|
|
return "T%i" + self.SPACE() + "M06"
|
|
|
|
def TOOL_DEFINITION(self):
|
|
return "G10" + self.SPACE() + "L1"
|
|
|
|
def WORKPLANE(self):
|
|
return "G%i"
|
|
|
|
def WORKPLANE_BASE(self):
|
|
return 53
|
|
|
|
def SPINDLE_CW(self):
|
|
return "M03"
|
|
|
|
def SPINDLE_CCW(self):
|
|
return "M04"
|
|
|
|
def COOLANT_OFF(self):
|
|
return "M09"
|
|
|
|
def COOLANT_MIST(self):
|
|
return "M07"
|
|
|
|
def COOLANT_FLOOD(self):
|
|
return "M08"
|
|
|
|
def GEAR_OFF(self):
|
|
return "?"
|
|
|
|
def GEAR(self):
|
|
return "M%i"
|
|
|
|
def GEAR_BASE(self):
|
|
return 37
|
|
|
|
def RAPID(self):
|
|
return "G00"
|
|
|
|
def FEED(self):
|
|
return "G01"
|
|
|
|
def ARC_CW(self):
|
|
return "G02"
|
|
|
|
def ARC_CCW(self):
|
|
return "G03"
|
|
|
|
def DWELL(self, dwell):
|
|
return "G04" + self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))
|
|
|
|
def DRILL(self):
|
|
return "G81"
|
|
|
|
def DRILL_WITH_DWELL(self, dwell):
|
|
return "G82" + self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))
|
|
|
|
def PECK_DRILL(self):
|
|
return "G83"
|
|
|
|
def PECK_DEPTH(self, depth):
|
|
return "Q" + (self.fmt.string(depth))
|
|
|
|
def RETRACT(self, height):
|
|
return "R" + (self.fmt.string(height))
|
|
|
|
def END_CANNED_CYCLE(self):
|
|
return "G80"
|
|
|
|
def TAP(self):
|
|
return "G84"
|
|
|
|
def TAP_DEPTH(self, depth):
|
|
return "K" + (self.fmt.string(depth))
|
|
|
|
def INTERNAL_COOLANT_ON(self):
|
|
return "M18"
|
|
|
|
def INTERNAL_COOLANT_OFF(self):
|
|
return "M9"
|
|
|
|
def X(self):
|
|
return "X"
|
|
|
|
def Y(self):
|
|
return "Y"
|
|
|
|
def Z(self):
|
|
return "Z"
|
|
|
|
def A(self):
|
|
return "A"
|
|
|
|
def B(self):
|
|
return "B"
|
|
|
|
def C(self):
|
|
return "C"
|
|
|
|
def CENTRE_X(self):
|
|
return "I"
|
|
|
|
def CENTRE_Y(self):
|
|
return "J"
|
|
|
|
def CENTRE_Z(self):
|
|
return "K"
|
|
|
|
def RADIUS(self):
|
|
return "R"
|
|
|
|
def TIME(self):
|
|
return "P"
|
|
|
|
def PROBE_TOWARDS_WITH_SIGNAL(self):
|
|
return "G38.2"
|
|
|
|
def PROBE_TOWARDS_WITHOUT_SIGNAL(self):
|
|
return "G38.3"
|
|
|
|
def PROBE_AWAY_WITH_SIGNAL(self):
|
|
return "G38.4"
|
|
|
|
def PROBE_AWAY_WITHOUT_SIGNAL(self):
|
|
return "G38.5"
|
|
|
|
def MACHINE_COORDINATES(self):
|
|
return "G53"
|
|
|
|
def EXACT_PATH_MODE(self):
|
|
return "G61"
|
|
|
|
def EXACT_STOP_MODE(self):
|
|
return "G61.1"
|
|
|
|
def RETRACT_TO_CLEARANCE(self):
|
|
return "G98"
|
|
|
|
def RETRACT_TO_STANDOFF(self):
|
|
return "G99"
|
|
|
|
############################################################################
|
|
# Internals
|
|
def write(self, s):
|
|
if self.output_disabled == False:
|
|
nc.Creator.write(self, s)
|
|
if "\n" in s:
|
|
self.start_of_line = s[-1] == "\n"
|
|
|
|
def write_feedrate(self):
|
|
self.f.write(self)
|
|
|
|
def write_preps(self):
|
|
i = 0
|
|
if self.g_plane.str:
|
|
i += 1
|
|
self.g_plane.write(self)
|
|
for g in self.g_list:
|
|
if i > 0:
|
|
self.write("\n" if self.m_codes_on_their_own_line else self.SPACE())
|
|
self.write(g)
|
|
i += 1
|
|
self.g_list = []
|
|
|
|
def write_misc(self):
|
|
if len(self.m):
|
|
self.write("\n" if self.m_codes_on_their_own_line else self.SPACE())
|
|
self.write(self.m.pop())
|
|
|
|
def write_spindle(self):
|
|
if self.s.str:
|
|
self.write("\n" if self.m_codes_on_their_own_line else "")
|
|
self.s.write(self)
|
|
|
|
def output_fixture(self):
|
|
if self.current_fixture != self.fixture_wanted:
|
|
self.current_fixture = self.fixture_wanted
|
|
self.g_list.append("G" + str(self.current_fixture))
|
|
|
|
def increment_fixture(self):
|
|
for i in range(0, len(self.fixture_order) - 1):
|
|
if self.fixture_order[i] == self.fixture_wanted:
|
|
self.fixture_wanted = self.fixture_order[i + 1]
|
|
return
|
|
raise "too many fixtures wanted!"
|
|
|
|
def get_fixture(self):
|
|
return self.fixture_wanted
|
|
|
|
def set_fixture(self, fixture):
|
|
self.fixture_wanted = fixture
|
|
|
|
############################################################################
|
|
# Programs
|
|
|
|
def program_begin(self, id, name=""):
|
|
if self.use_this_program_id:
|
|
id = self.use_this_program_id
|
|
if self.PROGRAM() != None:
|
|
self.writem([(self.PROGRAM() % id), self.SPACE(), (self.COMMENT(name))])
|
|
self.write("\n")
|
|
self.program_id = id
|
|
self.program_name = name
|
|
|
|
def add_stock(self, type_name, params):
|
|
if self.output_cutviewer_comments:
|
|
self.write("(STOCK/" + type_name)
|
|
for param in params:
|
|
self.write(",")
|
|
self.write(str(param))
|
|
self.write(")\n")
|
|
|
|
def program_stop(self, optional=False):
|
|
if optional:
|
|
self.write(self.SPACE() + self.STOP_OPTIONAL() + "\n")
|
|
self.prev_g0123 = ""
|
|
else:
|
|
self.write(self.STOP() + "\n")
|
|
self.prev_g0123 = ""
|
|
|
|
def number_file(self, filename):
|
|
import tempfile
|
|
|
|
temp_filename = tempfile.gettempdir() + "/renumbering.txt"
|
|
|
|
# make a copy of file
|
|
f_in = open(filename, "r")
|
|
f_out = open(temp_filename, "w")
|
|
while True:
|
|
line = f_in.readline()
|
|
if len(line) == 0:
|
|
break
|
|
f_out.write(line)
|
|
f_in.close()
|
|
f_out.close()
|
|
|
|
# read copy
|
|
f_in = open(temp_filename, "r")
|
|
f_out = open(filename, "w")
|
|
n = self.start_block_number
|
|
while True:
|
|
line = f_in.readline()
|
|
|
|
if len(line) == 0:
|
|
break
|
|
f_out.write(self.BLOCK() % n + self.SPACE() + line)
|
|
n += self.block_number_increment
|
|
if self.block_number_restart_after != None:
|
|
if n >= self.block_number_restart_after:
|
|
n = self.start_block_number
|
|
f_in.close()
|
|
f_out.close()
|
|
|
|
def program_end(self):
|
|
if self.z_for_g53 != None:
|
|
self.write(
|
|
self.SPACE()
|
|
+ self.MACHINE_COORDINATES()
|
|
+ self.SPACE()
|
|
+ "Z"
|
|
+ self.fmt.string(self.z_for_g53)
|
|
+ "\n"
|
|
)
|
|
self.write(self.SPACE() + self.PROGRAM_END() + "\n")
|
|
|
|
if self.temp_file_to_append_on_close != None:
|
|
f_in = open(self.temp_file_to_append_on_close, "r")
|
|
while True:
|
|
line = f_in.readline()
|
|
if len(line) == 0:
|
|
break
|
|
self.write(line)
|
|
f_in.close()
|
|
|
|
self.file_close()
|
|
|
|
if self.output_block_numbers:
|
|
# number every line of the file afterwards
|
|
self.number_file(self.filename)
|
|
|
|
for f in self.subroutine_files:
|
|
self.number_file(f)
|
|
|
|
def flush_nc(self):
|
|
if len(self.g_list) == 0 and len(self.m) == 0:
|
|
return
|
|
self.write_preps()
|
|
self.write_misc()
|
|
self.write("\n")
|
|
|
|
############################################################################
|
|
# Subprograms
|
|
|
|
def make_subroutine_name(self, id):
|
|
s = self.filename
|
|
for i in reversed(range(0, len(s))):
|
|
if s[i] == ".":
|
|
return s[0:i] + "sub" + str(id) + s[i:]
|
|
|
|
# '.' not found
|
|
return s + "sub" + str(id)
|
|
|
|
def sub_begin(self, id, name=None):
|
|
if id == None:
|
|
if self.current_sub_id == None:
|
|
self.current_sub_id = self.program_id
|
|
self.current_sub_id += 1
|
|
id = self.current_sub_id
|
|
|
|
if name == None:
|
|
name = self.program_name + " subroutine " + str(id)
|
|
|
|
self.save_file = self.file
|
|
if self.subroutines_in_own_files:
|
|
new_name = self.make_subroutine_name(id)
|
|
self.file = open(new_name, "w")
|
|
self.subroutine_files.append(new_name)
|
|
else:
|
|
# use temporary file
|
|
import tempfile
|
|
|
|
temp_filename = tempfile.gettempdir() + "/subroutines.txt"
|
|
if self.temp_file_to_append_on_close == None:
|
|
self.temp_file_to_append_on_close = temp_filename
|
|
self.file = open(temp_filename, "w")
|
|
else:
|
|
self.file = open(temp_filename, "a")
|
|
|
|
if self.PROGRAM() != None:
|
|
self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name)))
|
|
self.write("\n")
|
|
|
|
def sub_call(self, id):
|
|
if id == None:
|
|
id = self.current_sub_id
|
|
self.write(self.SPACE() + (self.SUBPROG_CALL() % id) + "\n")
|
|
|
|
def sub_end(self):
|
|
self.write(self.SPACE() + self.SUBPROG_END() + "\n")
|
|
|
|
self.file.close()
|
|
self.file = self.save_file
|
|
|
|
def disable_output(self):
|
|
self.output_disabled = True
|
|
|
|
def enable_output(self):
|
|
self.output_disabled = False
|
|
|
|
############################################################################
|
|
# Settings
|
|
|
|
def imperial(self):
|
|
self.g_list.append(self.IMPERIAL())
|
|
self.fmt.number_of_decimal_places = 4
|
|
|
|
def metric(self):
|
|
self.g_list.append(self.METRIC())
|
|
self.fmt.number_of_decimal_places = 3
|
|
|
|
def absolute(self):
|
|
self.g_list.append(self.ABSOLUTE())
|
|
self.absolute_flag = True
|
|
|
|
def incremental(self):
|
|
self.g_list.append(self.INCREMENTAL())
|
|
self.absolute_flag = False
|
|
|
|
def polar(self, on=True):
|
|
if on:
|
|
self.g_list.append(self.POLAR_ON())
|
|
else:
|
|
self.g_list.append(self.POLAR_OFF())
|
|
|
|
def set_plane(self, plane):
|
|
if plane == 0:
|
|
self.g_plane.set(self.PLANE_XY())
|
|
elif plane == 1:
|
|
self.g_plane.set(self.PLANE_XZ())
|
|
elif plane == 2:
|
|
self.g_plane.set(self.PLANE_YZ())
|
|
|
|
def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None):
|
|
self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM()))
|
|
if x != None:
|
|
self.write(self.SPACE() + "X " + (self.fmt.string(x + self.shift_x)))
|
|
if y != None:
|
|
self.write(self.SPACE() + "Y " + (self.fmt.string(y + self.shift_y)))
|
|
if z != None:
|
|
self.write(self.SPACE() + "Z " + (self.fmt.string(z + self.shift_z)))
|
|
if a != None:
|
|
self.write(self.SPACE() + "A " + (self.fmt.string(a)))
|
|
if b != None:
|
|
self.write(self.SPACE() + "B " + (self.fmt.string(b)))
|
|
if c != None:
|
|
self.write(self.SPACE() + "C " + (self.fmt.string(c)))
|
|
self.write("\n")
|
|
|
|
def remove_temporary_origin(self):
|
|
self.write(self.SPACE() + (self.REMOVE_TEMPORARY_COORDINATE_SYSTEM()))
|
|
self.write("\n")
|
|
|
|
############################################################################
|
|
# new graphics origin- make a new coordinate system and snap it onto the geometry
|
|
# the toolpath generated should be translated
|
|
|
|
def translate(self, x=None, y=None, z=None):
|
|
self.shift_x = -x
|
|
self.shift_y = -y
|
|
self.shift_z = -z
|
|
|
|
############################################################################
|
|
# Tools
|
|
|
|
def tool_change(self, id):
|
|
if self.output_comment_before_tool_change:
|
|
if id in self.tool_defn_params:
|
|
self.comment("tool change to " + self.tool_defn_params[id]["name"])
|
|
|
|
if self.output_cutviewer_comments:
|
|
import cutviewer
|
|
|
|
if id in self.tool_defn_params:
|
|
cutviewer.tool_defn(self, id, self.tool_defn_params[id])
|
|
if (self.t != None) and (self.z_for_g53 != None):
|
|
self.write("G53 Z" + str(self.z_for_g53) + "\n")
|
|
self.write(self.SPACE() + (self.TOOL() % id))
|
|
if self.output_G43_on_tool_change_line == True:
|
|
self.write(self.SPACE() + "G43")
|
|
self.write("\n")
|
|
if self.output_h_and_d_at_tool_change == True:
|
|
if self.output_G43_on_tool_change_line == False:
|
|
self.write(self.SPACE() + "G43")
|
|
self.write(self.SPACE() + "D" + str(id) + self.SPACE() + "H" + str(id) + "\n")
|
|
self.t = id
|
|
self.move_done_since_tool_change = False
|
|
|
|
def tool_defn(self, id, name="", params=None):
|
|
self.tool_defn_params[id] = params
|
|
if self.output_tool_definitions:
|
|
self.write(self.SPACE() + self.TOOL_DEFINITION())
|
|
self.write(self.SPACE() + ("P%i" % id) + " ")
|
|
|
|
if params["diameter"] != None:
|
|
self.write(self.SPACE() + ("R%.3f" % (float(params["diameter"]) / 2)))
|
|
|
|
if params["cutting edge height"] != None:
|
|
self.write(self.SPACE() + "Z%.3f" % float(params["cutting edge height"]))
|
|
|
|
self.write("\n")
|
|
|
|
def offset_radius(self, id, radius=None):
|
|
pass
|
|
|
|
def offset_length(self, id, length=None):
|
|
pass
|
|
|
|
def current_tool(self):
|
|
return self.t
|
|
|
|
############################################################################
|
|
# Datums
|
|
|
|
def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None):
|
|
pass
|
|
|
|
def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None):
|
|
pass
|
|
|
|
# This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3
|
|
# These are selected by values from 1 to 9 inclusive.
|
|
def workplane(self, id):
|
|
if (id >= 1) and (id <= 6):
|
|
self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE()))
|
|
if (id >= 7) and (id <= 9):
|
|
self.g_list.append(
|
|
((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + (".%i" % (id - 6)))
|
|
)
|
|
|
|
############################################################################
|
|
## Rates + Modes
|
|
|
|
def feedrate(self, f):
|
|
self.f.set(f)
|
|
self.fhv = False
|
|
|
|
def feedrate_hv(self, fh, fv):
|
|
self.fh = fh
|
|
self.fv = fv
|
|
self.fhv = True
|
|
|
|
def calc_feedrate_hv(self, h, v):
|
|
if math.fabs(v) > math.fabs(h * 2):
|
|
# some horizontal, so it should be fine to use the horizontal feed rate
|
|
self.f.set(self.fv)
|
|
else:
|
|
# not much, if any horizontal component, so use the vertical feed rate
|
|
self.f.set(self.fh)
|
|
|
|
# def spindle(self, s, clockwise):
|
|
# if clockwise == True:
|
|
# self.s.set(s, self.SPINDLE_CW(), self.SPINDLE_CCW())
|
|
# else:
|
|
# self.s.set(s, self.SPINDLE_CCW(), self.SPINDLE_CW())
|
|
def spindle(self, s, clockwise): # EXPERIMENTAL -- grbl only
|
|
# Get machine settings
|
|
machine = bpy.context.scene.cam_machine
|
|
|
|
if (
|
|
machine.spindle_slow_start_enable
|
|
and s > machine.spindle_min + machine.spindle_slow_start_skip_threshold
|
|
):
|
|
# Generate slow start sequence
|
|
steps = machine.spindle_slow_start_steps
|
|
total_time = machine.spindle_slow_start_total_time
|
|
step_time = total_time / steps
|
|
|
|
# Calculate speed increments
|
|
speed_range = s - machine.spindle_min
|
|
step_increment = speed_range / steps
|
|
|
|
# Start at minimum speed
|
|
current_speed = machine.spindle_min
|
|
if clockwise:
|
|
self.s.set(current_speed, self.SPINDLE_CW(), self.SPINDLE_CCW())
|
|
else:
|
|
self.s.set(current_speed, self.SPINDLE_CCW(), self.SPINDLE_CW())
|
|
self.write_spindle()
|
|
|
|
# Ramp up through intermediate steps
|
|
for step in range(1, steps + 1):
|
|
if step < steps:
|
|
current_speed = machine.spindle_min + (step_increment * step)
|
|
else:
|
|
current_speed = s # Final target speed
|
|
|
|
# Dwell for step time
|
|
self.dwell(step_time)
|
|
|
|
# Set next speed
|
|
current_speed = int(current_speed)
|
|
if clockwise:
|
|
self.s.set(current_speed, self.SPINDLE_CW(), self.SPINDLE_CCW())
|
|
else:
|
|
self.s.set(current_speed, self.SPINDLE_CCW(), self.SPINDLE_CW())
|
|
self.write_spindle()
|
|
else:
|
|
# Normal spindle start (existing code)
|
|
if clockwise:
|
|
self.s.set(s, self.SPINDLE_CW(), self.SPINDLE_CCW())
|
|
else:
|
|
self.s.set(s, self.SPINDLE_CCW(), self.SPINDLE_CW())
|
|
|
|
def coolant(self, mode=0):
|
|
if mode <= 0:
|
|
self.m.append(self.COOLANT_OFF())
|
|
elif mode == 1:
|
|
self.m.append(self.COOLANT_MIST())
|
|
elif mode == 2:
|
|
self.m.append(self.COOLANT_FLOOD())
|
|
|
|
def gearrange(self, gear=0):
|
|
if gear <= 0:
|
|
self.m.append(self.GEAR_OFF())
|
|
elif gear <= 4:
|
|
self.m.append(self.GEAR() % (gear + GEAR_BASE()))
|
|
|
|
############################################################################
|
|
# Moves
|
|
|
|
def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None):
|
|
(x, y, z, a, b, c, axis_count) = self.filter_xyz(x, y, z, a, b, c)
|
|
if axis_count == 0:
|
|
return
|
|
self.on_move()
|
|
|
|
if self.g0123_modal:
|
|
if self.prev_g0123 != self.RAPID():
|
|
self.write(self.SPACE() + self.RAPID())
|
|
self.prev_g0123 = self.RAPID()
|
|
else:
|
|
self.write(self.SPACE() + self.RAPID())
|
|
self.write_preps()
|
|
if x != None:
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x)))
|
|
else:
|
|
dx = x - self.x
|
|
self.write(self.SPACE() + self.X() + (self.fmt.string(dx)))
|
|
self.x = x
|
|
if y != None:
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y)))
|
|
else:
|
|
dy = y - self.y
|
|
self.write(self.SPACE() + self.Y() + (self.fmt.string(dy)))
|
|
|
|
self.y = y
|
|
if z != None:
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z)))
|
|
else:
|
|
dz = z - self.z
|
|
self.write(self.SPACE() + self.Z() + (self.fmt.string(dz)))
|
|
|
|
self.z = z
|
|
|
|
if a != None:
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.A() + (self.fmt.string(a)))
|
|
else:
|
|
da = a - self.a
|
|
self.write(self.SPACE() + self.A() + (self.fmt.string(da)))
|
|
self.a = a
|
|
|
|
if b != None:
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.B() + (self.fmt.string(b)))
|
|
else:
|
|
db = b - self.b
|
|
self.write(self.SPACE() + self.B() + (self.fmt.string(db)))
|
|
self.b = b
|
|
|
|
if c != None:
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.C() + (self.fmt.string(c)))
|
|
else:
|
|
dc = c - self.c
|
|
self.write(self.SPACE() + self.C() + (self.fmt.string(dc)))
|
|
self.c = c
|
|
self.write_spindle()
|
|
self.write_misc()
|
|
self.write("\n")
|
|
|
|
def feed(self, x=None, y=None, z=None, a=None, b=None, c=None):
|
|
(x, y, z, a, b, c, axis_count) = self.filter_xyz(x, y, z, a, b, c)
|
|
if axis_count == 0:
|
|
return
|
|
self.on_move()
|
|
if self.g0123_modal:
|
|
if self.prev_g0123 != self.FEED():
|
|
self.writem([self.SPACE(), self.FEED()])
|
|
self.prev_g0123 = self.FEED()
|
|
else:
|
|
self.write(self.SPACE() + self.FEED())
|
|
self.write_preps()
|
|
dx = dy = dz = 0
|
|
if x != None:
|
|
dx = x - self.x
|
|
if self.absolute_flag:
|
|
self.writem([self.SPACE(), self.X(), (self.fmt.string(x + self.shift_x))])
|
|
else:
|
|
self.writem([self.SPACE(), self.X(), (self.fmt.string(dx))])
|
|
self.x = x
|
|
if y != None:
|
|
dy = y - self.y
|
|
if self.absolute_flag:
|
|
self.writem([self.SPACE(), self.Y(), (self.fmt.string(y + self.shift_y))])
|
|
else:
|
|
self.writem([self.SPACE(), self.Y(), (self.fmt.string(dy))])
|
|
|
|
self.y = y
|
|
if z != None:
|
|
dz = z - self.z
|
|
if self.absolute_flag:
|
|
self.writem([self.SPACE(), self.Z(), (self.fmt.string(z + self.shift_z))])
|
|
else:
|
|
self.writem([self.SPACE(), self.Z(), (self.fmt.string(dz))])
|
|
|
|
self.z = z
|
|
|
|
if a != None:
|
|
da = a - self.a
|
|
if self.absolute_flag:
|
|
self.writem([self.SPACE(), self.A(), (self.fmt.string(a))])
|
|
else:
|
|
self.writem([self.SPACE(), self.A(), (self.fmt.string(da))])
|
|
self.a = a
|
|
|
|
if b != None:
|
|
db = b - self.b
|
|
if self.absolute_flag:
|
|
self.writem([self.SPACE(), self.B(), (self.fmt.string(b))])
|
|
else:
|
|
self.writem([self.SPACE(), self.B(), (self.fmt.string(db))])
|
|
self.b = b
|
|
|
|
if c != None:
|
|
dc = c - self.c
|
|
if self.absolute_flag:
|
|
self.writem([self.SPACE(), self.C(), (self.fmt.string(c))])
|
|
else:
|
|
self.writem([self.SPACE(), self.C(), (self.fmt.string(dc))])
|
|
self.c = c
|
|
|
|
if self.fhv:
|
|
self.calc_feedrate_hv(math.sqrt(dx * dx + dy * dy), math.fabs(dz))
|
|
self.write_feedrate()
|
|
self.write_spindle()
|
|
self.write_misc()
|
|
self.write("\n")
|
|
|
|
def filter_xyz(self, x=None, y=None, z=None, a=None, b=None, c=None):
|
|
"""Check if x,y,z,a,b,c are the same and set them to None if they are
|
|
return value = (x,y,z,a,b,c,count) where count is the number of
|
|
axis moves left.
|
|
"""
|
|
rv = [x, y, z, a, b, c, 0]
|
|
comparisons = (
|
|
(x, self.shift_x, self.x),
|
|
(y, self.shift_y, self.y),
|
|
(z, self.shift_z, self.z),
|
|
(a, 0, self.a),
|
|
(b, 0, self.b),
|
|
(c, 0, self.c),
|
|
)
|
|
|
|
for i, (new_val, shift, current_val) in enumerate(comparisons):
|
|
if new_val is not None:
|
|
if self.fmt.string(new_val + shift) == self.fmt.string(current_val):
|
|
rv[i] = None
|
|
else:
|
|
rv[6] += 1
|
|
return rv
|
|
|
|
def get_quadrant(self, dx, dy):
|
|
if dx < 0:
|
|
if dy < 0:
|
|
return 2
|
|
else:
|
|
return 1
|
|
|
|
else:
|
|
if dy < 0:
|
|
return 3
|
|
else:
|
|
return 0
|
|
|
|
def quadrant_start(self, q, i, j, rad):
|
|
while q > 3:
|
|
q = q - 4
|
|
if q == 0:
|
|
return i + rad, j
|
|
if q == 1:
|
|
return i, j + rad
|
|
if q == 2:
|
|
return i - rad, j
|
|
return i, j - rad
|
|
|
|
def quadrant_end(self, q, i, j, rad):
|
|
return self.quadrant_start(q + 1, i, j, rad)
|
|
|
|
def get_arc_angle(self, sdx, sdy, edx, edy, cw):
|
|
angle_s = math.atan2(sdy, sdx)
|
|
angle_e = math.atan2(edy, edx)
|
|
if cw:
|
|
if angle_s < angle_e:
|
|
angle_s = angle_s + 2 * math.pi
|
|
else:
|
|
if angle_e < angle_s:
|
|
angle_e = angle_e + 2 * math.pi
|
|
return angle_e - angle_s
|
|
|
|
def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None):
|
|
(x, y, z, _, _, _, axis_count) = self.filter_xyz(x, y, z)
|
|
if axis_count == 0:
|
|
return
|
|
|
|
if self.output_arcs_as_lines or (
|
|
self.can_do_helical_arcs == False
|
|
and self.in_quadrant_splitting == False
|
|
and (z != None)
|
|
and (math.fabs(z - self.z) > 0.000001)
|
|
and (self.fmt.string(z) != self.fmt.string(self.z))
|
|
):
|
|
# split the helical arc into little line feed moves
|
|
|
|
if x == None:
|
|
x = self.x
|
|
if y == None:
|
|
y = self.y
|
|
sdx = self.x - i
|
|
sdy = self.y - j
|
|
edx = x - i
|
|
edy = y - j
|
|
radius = math.sqrt(sdx * sdx + sdy * sdy)
|
|
arc_angle = self.get_arc_angle(sdx, sdy, edx, edy, cw)
|
|
angle_start = math.atan2(sdy, sdx)
|
|
tolerance = 0.02
|
|
angle_step = 2.0 * math.atan(math.sqrt(tolerance / (radius - tolerance)))
|
|
segments = int(math.fabs(arc_angle / angle_step) + 1)
|
|
angle_step = arc_angle / segments
|
|
angle = angle_start
|
|
if z != None:
|
|
z_step = float(z - self.z) / segments
|
|
next_z = self.z
|
|
|
|
for p in range(0, segments):
|
|
angle = angle + angle_step
|
|
next_x = i + radius * math.cos(angle)
|
|
next_y = j + radius * math.sin(angle)
|
|
if z == None:
|
|
next_z = None
|
|
else:
|
|
next_z = next_z + z_step
|
|
self.feed(next_x, next_y, next_z)
|
|
return
|
|
|
|
if self.arc_centre_positive == True and self.in_quadrant_splitting == False:
|
|
# split in to quadrant arcs
|
|
self.in_quadrant_splitting = True
|
|
|
|
if x == None:
|
|
x = self.x
|
|
if y == None:
|
|
y = self.y
|
|
sdx = self.x - i
|
|
sdy = self.y - j
|
|
edx = x - i
|
|
edy = y - j
|
|
|
|
qs = self.get_quadrant(sdx, sdy)
|
|
qe = self.get_quadrant(edx, edy)
|
|
|
|
if qs == qe:
|
|
arc_angle = math.fabs(self.get_arc_angle(sdx, sdy, edx, edy, cw))
|
|
# arc_angle will be either less than pi/2 or greater than 3pi/2
|
|
if arc_angle > 3.14:
|
|
if cw:
|
|
qs = qs + 4
|
|
else:
|
|
qe = qe + 4
|
|
|
|
if qs == qe:
|
|
self.arc(cw, x, y, z, i, j, k, r)
|
|
else:
|
|
rad = math.sqrt(sdx * sdx + sdy * sdy)
|
|
if cw:
|
|
if qs < qe:
|
|
qs = qs + 4
|
|
else:
|
|
if qe < qs:
|
|
qe = qe + 4
|
|
|
|
q = qs
|
|
while 1:
|
|
x1 = x
|
|
y1 = y
|
|
if q != qe:
|
|
if cw:
|
|
x1, y1 = self.quadrant_start(q, i, j, rad)
|
|
else:
|
|
x1, y1 = self.quadrant_end(q, i, j, rad)
|
|
|
|
if (self.fmt.string(x1) != self.fmt.string(self.x)) or (
|
|
self.fmt.string(y1) != self.fmt.string(self.y)
|
|
):
|
|
if (math.fabs(x1 - self.x) > 0.01) or (math.fabs(y1 - self.y) > 0.01):
|
|
self.arc(cw, x1, y1, z, i, j, k, r)
|
|
else:
|
|
self.feed(x1, y1, z)
|
|
if q == qe:
|
|
break
|
|
if cw:
|
|
q = q - 1
|
|
else:
|
|
q = q + 1
|
|
|
|
self.in_quadrant_splitting = False
|
|
return
|
|
|
|
self.on_move()
|
|
arc_g_code = ""
|
|
if cw:
|
|
arc_g_code = self.ARC_CW()
|
|
else:
|
|
arc_g_code = self.ARC_CCW()
|
|
if self.g0123_modal:
|
|
if self.prev_g0123 != arc_g_code:
|
|
self.write(self.SPACE() + arc_g_code)
|
|
self.prev_g0123 = arc_g_code
|
|
else:
|
|
self.write(self.SPACE() + arc_g_code)
|
|
self.write_preps()
|
|
if x != None:
|
|
dx = x - self.x
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x)))
|
|
else:
|
|
self.write(self.SPACE() + self.X() + (self.fmt.string(dx)))
|
|
if y != None:
|
|
dy = y - self.y
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y)))
|
|
else:
|
|
self.write(self.SPACE() + self.Y() + (self.fmt.string(dy)))
|
|
if z != None:
|
|
dz = z - self.z
|
|
if self.absolute_flag:
|
|
self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z)))
|
|
else:
|
|
self.write(self.SPACE() + self.Z() + (self.fmt.string(dz)))
|
|
if i != None:
|
|
if self.arc_centre_absolute == False:
|
|
i = i - self.x
|
|
s = self.fmt.string(i)
|
|
if self.arc_centre_positive == True:
|
|
if s[0] == "-":
|
|
s = s[1:]
|
|
self.write(self.SPACE() + self.CENTRE_X() + s)
|
|
if j != None:
|
|
if self.arc_centre_absolute == False:
|
|
j = j - self.y
|
|
s = self.fmt.string(j)
|
|
if self.arc_centre_positive == True:
|
|
if s[0] == "-":
|
|
s = s[1:]
|
|
self.write(self.SPACE() + self.CENTRE_Y() + s)
|
|
if k != None:
|
|
if self.arc_centre_absolute == False:
|
|
k = k - self.z
|
|
s = self.fmt.string(k)
|
|
if self.arc_centre_positive == True:
|
|
if s[0] == "-":
|
|
s = s[1:]
|
|
self.write(self.SPACE() + self.CENTRE_Z() + s)
|
|
if r != None:
|
|
s = self.fmt.string(r)
|
|
if self.arc_centre_positive == True:
|
|
if s[0] == "-":
|
|
s = s[1:]
|
|
self.write(self.SPACE() + self.RADIUS() + s)
|
|
# use horizontal feed rate
|
|
if self.fhv:
|
|
self.calc_feedrate_hv(1, 0)
|
|
self.write_feedrate()
|
|
self.write_spindle()
|
|
self.write_misc()
|
|
self.write("\n")
|
|
if x != None:
|
|
self.x = x
|
|
if y != None:
|
|
self.y = y
|
|
if z != None:
|
|
self.z = z
|
|
|
|
def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None):
|
|
self.arc(True, x, y, z, i, j, k, r)
|
|
|
|
def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None):
|
|
self.arc(False, x, y, z, i, j, k, r)
|
|
|
|
def dwell(self, t):
|
|
self.write_preps()
|
|
self.write(self.SPACE() + self.DWELL(t))
|
|
self.write_misc()
|
|
self.write("\n")
|
|
|
|
def on_move(self):
|
|
if self.output_fixtures:
|
|
self.output_fixture()
|
|
self.move_done_since_tool_change = True
|
|
|
|
def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None):
|
|
pass
|
|
|
|
def rapid_unhome(self):
|
|
pass
|
|
|
|
def set_machine_coordinates(self):
|
|
self.write(self.SPACE() + self.MACHINE_COORDINATES())
|
|
self.prev_g0123 = ""
|
|
|
|
############################################################################
|
|
# CRC
|
|
|
|
def use_CRC(self):
|
|
return self.useCrc
|
|
|
|
def CRC_nominal_path(self):
|
|
return self.useCrcCenterline
|
|
|
|
def start_CRC(self, left=True, radius=0.0):
|
|
# set up prep code, to be output on next line
|
|
if self.t == None:
|
|
raise "No tool specified for start_CRC()"
|
|
if left:
|
|
self.write(self.SPACE() + "G41")
|
|
else:
|
|
self.write(self.SPACE() + "G42")
|
|
self.write((self.SPACE() + "D%i\n") % self.t)
|
|
|
|
def end_CRC(self):
|
|
self.write(self.SPACE() + "G40\n")
|
|
|
|
############################################################################
|
|
# Cycles
|
|
|
|
def pattern(self):
|
|
pass
|
|
|
|
def pattern_uses_subroutine(self):
|
|
return self.pattern_done_with_subroutine
|
|
|
|
def pocket(self):
|
|
pass
|
|
|
|
def profile(self):
|
|
pass
|
|
|
|
def write_internal_coolant_commands(self, internal_coolant_on):
|
|
if (internal_coolant_on != None) and (self.output_internal_coolant_commands == True):
|
|
if internal_coolant_on == True:
|
|
if self.internal_coolant_on != True:
|
|
self.write(self.SPACE())
|
|
self.write(self.INTERNAL_COOLANT_ON() + "\n")
|
|
self.internal_coolant_on = True
|
|
else:
|
|
if self.internal_coolant_on != False:
|
|
self.write(self.SPACE())
|
|
self.write(self.INTERNAL_COOLANT_OFF() + "\n")
|
|
self.internal_coolant_on = False
|
|
|
|
# The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83).
|
|
# The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to
|
|
# the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole.
|
|
# Instead, this routine combines the Z value and the depth value to determine the bottom of
|
|
# the hole.
|
|
#
|
|
# The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts
|
|
# to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles.
|
|
#
|
|
# The peck_depth value is the incremental depth (Q value) that tells the peck drilling
|
|
# cycle how deep to go on each peck until the full depth is achieved.
|
|
#
|
|
# NOTE: This routine forces the mode to absolute mode so that the values passed into
|
|
# the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't
|
|
# revert it. I must set the mode so that I can be sure the values I'm passing in make
|
|
# sense to the end-machine.
|
|
#
|
|
def drill(
|
|
self,
|
|
x=None,
|
|
y=None,
|
|
dwell=None,
|
|
depthparams=None,
|
|
retract_mode=None,
|
|
spindle_mode=None,
|
|
internal_coolant_on=None,
|
|
rapid_to_clearance=None,
|
|
):
|
|
if depthparams.clearance_height == None:
|
|
self.first_drill_pos = False
|
|
return
|
|
|
|
self.write_internal_coolant_commands(internal_coolant_on)
|
|
|
|
drillExpanded = self.drillExpanded
|
|
if (depthparams.step_down != 0) and (dwell != 0):
|
|
# pecking and dwell together
|
|
if self.dwell_allowed_in_G83 != True:
|
|
drillExpanded = True
|
|
|
|
if drillExpanded:
|
|
# for machines which don't understand G81, G82 etc.
|
|
peck_depth = depthparams.step_down
|
|
if peck_depth == None:
|
|
peck_depth = depthparams.final_depth
|
|
current_z = depthparams.start_depth
|
|
self.rapid(x, y)
|
|
|
|
first = True
|
|
last_cut = False
|
|
|
|
while True:
|
|
next_z = current_z - peck_depth
|
|
if next_z < (depthparams.final_depth + 0.001):
|
|
next_z = depthparams.final_depth
|
|
last_cut = True
|
|
if next_z >= current_z:
|
|
break
|
|
if first:
|
|
self.rapid(z=depthparams.start_depth + depthparams.rapid_safety_space)
|
|
else:
|
|
self.rapid(z=current_z)
|
|
self.feed(z=next_z)
|
|
if dwell != 0 and last_cut:
|
|
self.dwell(dwell)
|
|
if last_cut:
|
|
self.rapid(z=depthparams.clearance_height)
|
|
else:
|
|
if rapid_to_clearance:
|
|
self.rapid(z=depthparams.clearance_height)
|
|
else:
|
|
self.rapid(z=depthparams.start_depth + depthparams.rapid_safety_space)
|
|
current_z = next_z
|
|
first = False
|
|
|
|
self.first_drill_pos = False
|
|
return
|
|
|
|
if self.output_g98_and_g99 == True:
|
|
if rapid_to_clearance == True:
|
|
if self.output_g43_z_before_drilling_if_g98:
|
|
if self.fmt.string(depthparams.clearance_height) != self.z_for_g43:
|
|
self.z_for_g43 = self.fmt.string(depthparams.clearance_height)
|
|
self.write(
|
|
self.SPACE() + "G43" + self.SPACE() + "Z" + self.z_for_g43 + "\n"
|
|
)
|
|
|
|
if self.first_drill_pos == True and rapid_to_clearance == True:
|
|
self.rapid(x, y)
|
|
self.rapid(z=depthparams.clearance_height)
|
|
|
|
self.in_canned_cycle = True
|
|
self.write_preps()
|
|
|
|
if depthparams.step_down != 0:
|
|
# G83 peck drilling
|
|
if self.drill_modal:
|
|
if self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down) != self.prev_drill:
|
|
self.write(
|
|
self.SPACE()
|
|
+ self.PECK_DRILL()
|
|
+ self.SPACE()
|
|
+ self.PECK_DEPTH(depthparams.step_down)
|
|
)
|
|
self.prev_drill = self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down)
|
|
else:
|
|
self.write(self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down))
|
|
|
|
if (self.dwell != 0) and self.dwell_allowed_in_G83:
|
|
self.write(self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell)))
|
|
|
|
else:
|
|
# We're either just drilling or drilling with dwell.
|
|
if dwell == 0:
|
|
# We're just drilling.
|
|
if self.drill_modal:
|
|
if self.DRILL() != self.prev_drill:
|
|
self.write(self.SPACE() + self.DRILL())
|
|
self.prev_drill = self.DRILL()
|
|
else:
|
|
self.write(self.SPACE() + self.DRILL())
|
|
|
|
else:
|
|
# We're drilling with dwell.
|
|
|
|
if self.drill_modal:
|
|
if self.DRILL_WITH_DWELL(dwell) != self.prev_drill:
|
|
self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell))
|
|
self.prev_drill = self.DRILL_WITH_DWELL(dwell)
|
|
else:
|
|
self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell))
|
|
|
|
if self.output_g98_and_g99 == True:
|
|
if rapid_to_clearance == True:
|
|
if self.g98_not_g99 != True:
|
|
self.write(self.SPACE() + self.RETRACT_TO_CLEARANCE())
|
|
self.g98_not_g99 = True
|
|
else:
|
|
if self.g98_not_g99 != False:
|
|
self.write(self.SPACE() + self.RETRACT_TO_STANDOFF())
|
|
self.g98_not_g99 = False
|
|
|
|
# Set the retraction point to the 'standoff' distance above the starting z height.
|
|
retract_height = depthparams.start_depth + depthparams.rapid_safety_space
|
|
if x != None:
|
|
self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x)))
|
|
self.x = x
|
|
|
|
if y != None:
|
|
self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y)))
|
|
self.y = y
|
|
|
|
if self.drill_modal:
|
|
if depthparams.start_depth != self.prev_z:
|
|
self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth)))
|
|
self.prev_z = depthparams.start_depth
|
|
else:
|
|
# This is the 'z' value for the bottom of the hole.
|
|
self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth)))
|
|
# We want to remember where z is at the end (at the top of the hole)
|
|
self.z = depthparams.start_depth + depthparams.rapid_safety_space
|
|
|
|
if self.drill_modal:
|
|
if self.prev_retract != self.RETRACT(retract_height):
|
|
self.write(self.SPACE() + self.RETRACT(retract_height))
|
|
self.prev_retract = self.RETRACT(retract_height)
|
|
else:
|
|
self.write(self.SPACE() + self.RETRACT(retract_height))
|
|
|
|
if self.fv:
|
|
self.f.set(self.fv)
|
|
|
|
self.write_feedrate()
|
|
self.write_spindle()
|
|
self.write_misc()
|
|
self.write("\n")
|
|
self.first_drill_pos = False
|
|
|
|
def end_canned_cycle(self):
|
|
if self.in_canned_cycle == False:
|
|
return
|
|
self.write(self.SPACE() + self.END_CANNED_CYCLE() + "\n")
|
|
self.write_internal_coolant_commands(0)
|
|
self.prev_drill = ""
|
|
self.prev_g0123 = ""
|
|
self.prev_z = ""
|
|
self.prev_f = ""
|
|
self.prev_retract = ""
|
|
self.in_canned_cycle = False
|
|
self.first_drill_pos = True
|
|
|
|
############################################################################
|
|
# Misc
|
|
|
|
def comment(self, text):
|
|
self.write((self.COMMENT(text) + "\n"))
|
|
|
|
def insert(self, text):
|
|
pass
|
|
|
|
def block_delete(self, on=False):
|
|
pass
|
|
|
|
def variable(self, id):
|
|
return self.VARIABLE() % id
|
|
|
|
def variable_set(self, id, value):
|
|
self.write(
|
|
self.SPACE()
|
|
+ (self.VARIABLE() % id)
|
|
+ self.SPACE()
|
|
+ (self.VARIABLE_SET() % value)
|
|
+ "\n"
|
|
)
|
|
|
|
# This routine uses the G92 coordinate system offsets to establish a temporary coordinate
|
|
# system at the machine's current position. It can then use absolute coordinates relative
|
|
# to this position which makes coding easy. It then moves to the 'point along edge' which
|
|
# should be above the workpiece but still on one edge. It then backs off from the edge
|
|
# to the 'retracted point'. It then plunges down by the depth value specified. It then
|
|
# probes back towards the 'destination point'. The probed X,Y location are stored
|
|
# into the 'intersection variable' variables. Finally the machine moves back to the
|
|
# original location. This is important so that the results of multiple calls to this
|
|
# routine may be compared meaningfully.
|
|
def probe_single_point(
|
|
self,
|
|
point_along_edge_x=None,
|
|
point_along_edge_y=None,
|
|
depth=None,
|
|
retracted_point_x=None,
|
|
retracted_point_y=None,
|
|
destination_point_x=None,
|
|
destination_point_y=None,
|
|
intersection_variable_x=None,
|
|
intersection_variable_y=None,
|
|
probe_offset_x_component=None,
|
|
probe_offset_y_component=None,
|
|
):
|
|
self.write(
|
|
self.SPACE()
|
|
+ (
|
|
self.SET_TEMPORARY_COORDINATE_SYSTEM()
|
|
+ (" X 0 Y 0 Z 0")
|
|
+ ("\t(Temporarily make this the origin)\n")
|
|
)
|
|
)
|
|
|
|
if self.fhv:
|
|
self.calc_feedrate_hv(1, 0)
|
|
self.write_feedrate()
|
|
self.write("\t(Set the feed rate for probing)\n")
|
|
|
|
self.rapid(point_along_edge_x, point_along_edge_y)
|
|
self.rapid(retracted_point_x, retracted_point_y)
|
|
self.feed(z=depth)
|
|
|
|
self.write(
|
|
(
|
|
self.PROBE_TOWARDS_WITH_SIGNAL()
|
|
+ (
|
|
" X "
|
|
+ (self.fmt.string(destination_point_x))
|
|
+ " Y "
|
|
+ (self.fmt.string(destination_point_y))
|
|
)
|
|
+ ("\t(Probe towards our destination point)\n")
|
|
)
|
|
)
|
|
|
|
self.comment("Back off the workpiece and re-probe more slowly")
|
|
self.write(
|
|
self.SPACE()
|
|
+ (
|
|
"#"
|
|
+ intersection_variable_x
|
|
+ "= [#5061 - [ 0.5 * "
|
|
+ probe_offset_x_component
|
|
+ "]]\n"
|
|
)
|
|
)
|
|
self.write(
|
|
self.SPACE()
|
|
+ (
|
|
"#"
|
|
+ intersection_variable_y
|
|
+ "= [#5062 - [ 0.5 * "
|
|
+ probe_offset_y_component
|
|
+ "]]\n"
|
|
)
|
|
)
|
|
self.write(self.RAPID())
|
|
self.write(
|
|
self.SPACE()
|
|
+ " X #"
|
|
+ intersection_variable_x
|
|
+ " Y #"
|
|
+ intersection_variable_y
|
|
+ "\n"
|
|
)
|
|
|
|
self.write(self.SPACE() + self.FEEDRATE() + self.ffmt.string(self.fh / 2.0) + "\n")
|
|
|
|
self.write(
|
|
(
|
|
self.SPACE()
|
|
+ self.PROBE_TOWARDS_WITH_SIGNAL()
|
|
+ (
|
|
" X "
|
|
+ (self.fmt.string(destination_point_x))
|
|
+ " Y "
|
|
+ (self.fmt.string(destination_point_y))
|
|
)
|
|
+ ("\t(Probe towards our destination point)\n")
|
|
)
|
|
)
|
|
|
|
self.comment("Store the probed location somewhere we can get it again later")
|
|
self.write(
|
|
(
|
|
"#"
|
|
+ intersection_variable_x
|
|
+ "="
|
|
+ probe_offset_x_component
|
|
+ " (Portion of probe radius that contributes to the X coordinate)\n"
|
|
)
|
|
)
|
|
self.write(
|
|
("#" + intersection_variable_x + "=[#" + intersection_variable_x + " + #5061]\n")
|
|
)
|
|
self.write(
|
|
(
|
|
"#"
|
|
+ intersection_variable_y
|
|
+ "="
|
|
+ probe_offset_y_component
|
|
+ " (Portion of probe radius that contributes to the Y coordinate)\n"
|
|
)
|
|
)
|
|
self.write(
|
|
("#" + intersection_variable_y + "=[#" + intersection_variable_y + " + #5062]\n")
|
|
)
|
|
|
|
self.comment("Now move back to the original location")
|
|
self.rapid(retracted_point_x, retracted_point_y)
|
|
self.rapid(z=0)
|
|
self.rapid(point_along_edge_x, point_along_edge_y)
|
|
self.rapid(x=0, y=0)
|
|
|
|
self.write(
|
|
(
|
|
self.REMOVE_TEMPORARY_COORDINATE_SYSTEM()
|
|
+ ("\t(Restore the previous coordinate system)\n")
|
|
)
|
|
)
|
|
|
|
def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None):
|
|
self.write(
|
|
(
|
|
self.SET_TEMPORARY_COORDINATE_SYSTEM()
|
|
+ (" X 0 Y 0 Z 0")
|
|
+ ("\t(Temporarily make this the origin)\n")
|
|
)
|
|
)
|
|
if self.fhv:
|
|
self.calc_feedrate_hv(1, 0)
|
|
self.write(self.FEEDRATE() + " [" + self.ffmt.string(self.fh) + " / 5.0 ]")
|
|
self.write("\t(Set the feed rate for probing)\n")
|
|
|
|
if x != None and y != None:
|
|
self.write(self.RAPID())
|
|
self.write(" X " + x + " Y " + y + "\n")
|
|
|
|
self.write(
|
|
(
|
|
self.PROBE_TOWARDS_WITH_SIGNAL()
|
|
+ " Z "
|
|
+ (self.fmt.string(depth))
|
|
+ ("\t(Probe towards our destination point)\n")
|
|
)
|
|
)
|
|
|
|
self.comment("Store the probed location somewhere we can get it again later")
|
|
self.write(("#" + intersection_variable_z + "= #5063\n"))
|
|
|
|
self.comment("Now move back to the original location")
|
|
self.rapid(z=0)
|
|
self.rapid(x=0, y=0)
|
|
|
|
self.write(
|
|
(
|
|
self.REMOVE_TEMPORARY_COORDINATE_SYSTEM()
|
|
+ ("\t(Restore the previous coordinate system)\n")
|
|
)
|
|
)
|
|
|
|
def report_probe_results(
|
|
self,
|
|
x1=None,
|
|
y1=None,
|
|
z1=None,
|
|
x2=None,
|
|
y2=None,
|
|
z2=None,
|
|
x3=None,
|
|
y3=None,
|
|
z3=None,
|
|
x4=None,
|
|
y4=None,
|
|
z4=None,
|
|
x5=None,
|
|
y5=None,
|
|
z5=None,
|
|
x6=None,
|
|
y6=None,
|
|
z6=None,
|
|
xml_file_name=None,
|
|
):
|
|
pass
|
|
|
|
def open_log_file(self, xml_file_name=None):
|
|
pass
|
|
|
|
def log_coordinate(self, x=None, y=None, z=None):
|
|
pass
|
|
|
|
def log_message(self, message=None):
|
|
pass
|
|
|
|
def close_log_file(self):
|
|
pass
|
|
|
|
# Rapid movement to the midpoint between the two points specified.
|
|
# NOTE: The points are specified either as strings representing numbers or as strings
|
|
# representing variable names. This allows the HeeksCNC module to determine which
|
|
# variable names are used in these various routines.
|
|
def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None):
|
|
self.write(self.RAPID())
|
|
if (x1 != None) and (x2 != None):
|
|
self.write((" X " + "[[[" + x1 + " - " + x2 + "] / 2.0] + " + x2 + "]"))
|
|
|
|
if (y1 != None) and (y2 != None):
|
|
self.write((" Y " + "[[[" + y1 + " - " + y2 + "] / 2.0] + " + y2 + "]"))
|
|
|
|
if (z1 != None) and (z2 != None):
|
|
self.write((" Z " + "[[[" + z1 + " - " + z2 + "] / 2.0] + " + z2 + "]"))
|
|
|
|
self.write("\n")
|
|
|
|
# Rapid movement to the intersection of two lines (in the XY plane only). This routine
|
|
# is based on information found in http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
|
|
# written by Paul Bourke. The ua_numerator, ua_denominator, ua and ub parameters
|
|
# represent variable names (with the preceding '#' included in them) for use as temporary
|
|
# variables. They're specified here simply so that HeeksCNC can manage which variables
|
|
# are used in which GCode calculations.
|
|
#
|
|
# As per the notes on the web page, the ua_denominator and ub_denominator formulae are
|
|
# the same so we don't repeat this. If the two lines are coincident or parallel then
|
|
# no movement occurs.
|
|
#
|
|
# NOTE: The points are specified either as strings representing numbers or as strings
|
|
# representing variable names. This allows the HeeksCNC module to determine which
|
|
# variable names are used in these various routines.
|
|
def rapid_to_intersection(
|
|
self,
|
|
x1,
|
|
y1,
|
|
x2,
|
|
y2,
|
|
x3,
|
|
y3,
|
|
x4,
|
|
y4,
|
|
intersection_x,
|
|
intersection_y,
|
|
ua_numerator,
|
|
ua_denominator,
|
|
ua,
|
|
ub_numerator,
|
|
ub,
|
|
):
|
|
self.comment("Find the intersection of the two lines made up by the four probed points")
|
|
self.write(
|
|
ua_numerator
|
|
+ "=[[["
|
|
+ x4
|
|
+ " - "
|
|
+ x3
|
|
+ "] * ["
|
|
+ y1
|
|
+ " - "
|
|
+ y3
|
|
+ "]] - [["
|
|
+ y4
|
|
+ " - "
|
|
+ y3
|
|
+ "] * ["
|
|
+ x1
|
|
+ " - "
|
|
+ x3
|
|
+ "]]]\n"
|
|
)
|
|
self.write(
|
|
ua_denominator
|
|
+ "=[[["
|
|
+ y4
|
|
+ " - "
|
|
+ y3
|
|
+ "] * ["
|
|
+ x2
|
|
+ " - "
|
|
+ x1
|
|
+ "]] - [["
|
|
+ x4
|
|
+ " - "
|
|
+ x3
|
|
+ "] * ["
|
|
+ y2
|
|
+ " - "
|
|
+ y1
|
|
+ "]]]\n"
|
|
)
|
|
self.write(
|
|
ub_numerator
|
|
+ "=[[["
|
|
+ x2
|
|
+ " - "
|
|
+ x1
|
|
+ "] * ["
|
|
+ y1
|
|
+ " - "
|
|
+ y3
|
|
+ "]] - [["
|
|
+ y2
|
|
+ " - "
|
|
+ y1
|
|
+ "] * ["
|
|
+ x1
|
|
+ " - "
|
|
+ x3
|
|
+ "]]]\n"
|
|
)
|
|
|
|
self.comment("If they are not parallel")
|
|
self.write("O900 IF [" + ua_denominator + " NE 0]\n")
|
|
self.comment("And if they are not coincident")
|
|
self.write("O901 IF [" + ua_numerator + " NE 0 ]\n")
|
|
|
|
self.write(" " + ua + "=[" + ua_numerator + " / " + ua_denominator + "]\n")
|
|
# NOTE: ub denominator is the same as ua denominator
|
|
self.write(" " + ub + "=[" + ub_numerator + " / " + ua_denominator + "]\n")
|
|
self.write(
|
|
" "
|
|
+ intersection_x
|
|
+ "=["
|
|
+ x1
|
|
+ " + [["
|
|
+ ua
|
|
+ " * ["
|
|
+ x2
|
|
+ " - "
|
|
+ x1
|
|
+ "]]]]\n"
|
|
)
|
|
self.write(
|
|
" "
|
|
+ intersection_y
|
|
+ "=["
|
|
+ y1
|
|
+ " + [["
|
|
+ ua
|
|
+ " * ["
|
|
+ y2
|
|
+ " - "
|
|
+ y1
|
|
+ "]]]]\n"
|
|
)
|
|
self.write(" " + self.RAPID())
|
|
self.write(" X " + intersection_x + " Y " + intersection_y + "\n")
|
|
|
|
self.write("O901 ENDIF\n")
|
|
self.write("O900 ENDIF\n")
|
|
|
|
# We need to calculate the rotation angle based on the line formed by the
|
|
# x1,y1 and x2,y2 coordinate pair. With that angle, we need to move
|
|
# x_offset and y_offset distance from the current (0,0,0) position.
|
|
#
|
|
# The x1,y1,x2 and y2 parameters are all variable names that contain the actual
|
|
# values.
|
|
# The x_offset and y_offset are both numeric (floating point) values
|
|
def rapid_to_rotated_coordinate(
|
|
self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final
|
|
):
|
|
self.comment("Rapid to rotated coordinate")
|
|
self.write(
|
|
"#1 = [atan[" + y2 + " - " + y1 + "]/[" + x2 + " - " + x1 + "]] (nominal_angle)\n"
|
|
)
|
|
self.write("#2 = [atan[" + ref_y + "]/[" + ref_x + "]] (reference angle)\n")
|
|
self.write("#3 = [#1 - #2] (angle)\n")
|
|
self.write(
|
|
"#4 = [[["
|
|
+ (self.fmt.string(0))
|
|
+ " - "
|
|
+ (self.fmt.string(x_current))
|
|
+ "] * COS[ #3 ]] - [["
|
|
+ (self.fmt.string(0))
|
|
+ " - "
|
|
+ (self.fmt.string(y_current))
|
|
+ "] * SIN[ #3 ]]]\n"
|
|
)
|
|
self.write(
|
|
"#5 = [[["
|
|
+ (self.fmt.string(0))
|
|
+ " - "
|
|
+ (self.fmt.string(x_current))
|
|
+ "] * SIN[ #3 ]] + [["
|
|
+ (self.fmt.string(0))
|
|
+ " - "
|
|
+ (self.fmt.string(y_current))
|
|
+ "] * COS[ #3 ]]]\n"
|
|
)
|
|
|
|
self.write(
|
|
"#6 = [["
|
|
+ (self.fmt.string(x_final))
|
|
+ " * COS[ #3 ]] - ["
|
|
+ (self.fmt.string(y_final))
|
|
+ " * SIN[ #3 ]]]\n"
|
|
)
|
|
self.write(
|
|
"#7 = [["
|
|
+ (self.fmt.string(y_final))
|
|
+ " * SIN[ #3 ]] + ["
|
|
+ (self.fmt.string(y_final))
|
|
+ " * COS[ #3 ]]]\n"
|
|
)
|
|
|
|
self.write(self.RAPID() + " X [ #4 + #6 ] Y [ #5 + #7 ]\n")
|
|
|
|
def BEST_POSSIBLE_SPEED(self, motion_blending_tolerance, naive_cam_tolerance):
|
|
statement = "G64"
|
|
|
|
if motion_blending_tolerance > 0:
|
|
statement += " P " + str(motion_blending_tolerance)
|
|
|
|
if naive_cam_tolerance > 0:
|
|
statement += " Q " + str(naive_cam_tolerance)
|
|
|
|
return statement
|
|
|
|
def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance):
|
|
if mode == 0:
|
|
self.write(self.EXACT_PATH_MODE() + "\n")
|
|
if mode == 1:
|
|
self.write(self.EXACT_STOP_MODE() + "\n")
|
|
if mode == 2:
|
|
self.write(
|
|
self.BEST_POSSIBLE_SPEED(motion_blending_tolerance, naive_cam_tolerance) + "\n"
|
|
)
|
|
|
|
|
|
################################################################################
|
|
|
|
nc.creator = Creator()
|