diff --git a/PathFeedsAndSpeeds.py b/PathFeedsAndSpeeds.py index b782d83..c6fb212 100755 --- a/PathFeedsAndSpeeds.py +++ b/PathFeedsAndSpeeds.py @@ -1,15 +1,12 @@ # Feed and Speed Calculator # Provides a basic feeds and speeds calculator for use with FreeCAD Path -import FreeCAD,FreeCADGui -import math +import math from bisect import bisect_right -from PySide import QtGui, QtCore -from PySide.QtGui import QApplication, QDialog, QMainWindow -import PathScripts.PathGui as PathGui +# Interpolate Example from: https://stackoverflow.com/questions/7343697/how-to-implement-linear-interpolation + -## Interpolate Example from: https://stackoverflow.com/questions/7343697/how-to-implement-linear-interpolation class Interpolate: def __init__(self, x_list, y_list): if any(y - x <= 0 for x, y in zip(x_list, x_list[1:])): @@ -27,6 +24,7 @@ def __call__(self, x): i = bisect_right(self.x_list, x) - 1 return self.y_list[i] + self.slopes[i] * (x - self.x_list[i]) + def getInterpolatedValue(inputDict, value): keys = list(inputDict.keys()) values = list(inputDict.values()) @@ -37,173 +35,178 @@ def getInterpolatedValue(inputDict, value): print("Interpolated value outside the expected range") return None + def load_materials(): - ## Data from Machineries Handbook 28. - ## Kp: Tables 1a, 1b - ## Brinell Hardness: http://www.matweb.com + # Data from Machineries Handbook 28. + # Kp: Tables 1a, 1b + # Brinell Hardness: http://www.matweb.com - ## ss_hss = surface speed (m/min) for milling with high speed steel tools (hss) - ## ss_cbd = surface speed (m/min) for milling with carbide tools - ## ss_drill_hss = surface speed (m/min) for drilling with high speed steel tools (hss) - ## ss_drill_cbd = surface speed (m/min) for drilling with carbide tools - ## Kd = workMaterialFactor from Table 31 - ## ref: 1 ft/min = 0.3048 m/min + # ss_hss = surface speed (m/min) for milling with high speed steel tools (hss) + # ss_cbd = surface speed (m/min) for milling with carbide tools + # ss_drill_hss = surface speed (m/min) for drilling with high speed steel tools (hss) + # ss_drill_cbd = surface speed (m/min) for drilling with carbide tools + # Kd = workMaterialFactor from Table 31 + # ref: 1 ft/min = 0.3048 m/min materials = [ - { "material": "Softwood", "ss_hss": 225, "ss_cbd": 255, "ss_drill_hss": 185, "ss_drill_cbd": 205, "kp": 0.5, "brinell": 0, "Kd": 3000 }, - { "material": "Hardwood", "ss_hss": 145, "ss_cbd": 275, "ss_drill_hss": 115, "ss_drill_cbd": 400, "kp": 0.75, "brinell": 0, "Kd": 4000 }, - { "material": "Soft Plastics", "ss_hss": 225, "ss_cbd": 255, "ss_drill_hss": 185, "ss_drill_cbd": 205, "kp": 0.5, "brinell": 0, "Kd": 2000 }, - { "material": "Hard Plastics", "ss_hss": 225, "ss_cbd": 275, "ss_drill_hss": 115, "ss_drill_cbd": 400, "kp": 0.75, "brinell": 0, "Kd": 2000 }, - { "material": "Aluminium (6061)", "ss_hss": 175, "ss_cbd": 395, "ss_drill_hss": 135, "ss_drill_cbd": 310, "kp": 0.90, "brinell": 95, "Kd": 7000 }, - { "material": "Aluminium (7075)", "ss_hss": 175, "ss_cbd": 395, "ss_drill_hss": 125, "ss_drill_cbd": 310, "kp": 0.90, "brinell": 150, "Kd": 7000 }, - { "material": "Aluminium (Cast)", "ss_hss": 175, "ss_cbd": 395, "ss_drill_hss": 135, "ss_drill_cbd": 310, "kp": 0.68, "brinell": 150, "Kd": 7000 }, - { "material": "Brass (Hard)", "ss_hss": 200, "ss_cbd": 395, "ss_drill_hss": 115, "ss_drill_cbd": 350, "kp": 2.27, "brinell": 120, "Kd": 14000 }, - { "material": "Brass (Medium)", "ss_hss": 175, "ss_cbd": 350, "ss_drill_hss": 115, "ss_drill_cbd": 350, "kp": 1.36, "brinell": 120, "Kd": 14000 }, - { "material": "Brass (Soft)", "ss_hss": 125, "ss_cbd": 300, "ss_drill_hss": 115, "ss_drill_cbd": 350, "kp": 0.68, "brinell": 120, "Kd": 7000 }, - { "material": "Carbon Steel", "ss_hss": 35, "ss_cbd": 120, "ss_drill_hss": 25, "ss_drill_cbd": 90, "kp": 1.88, "brinell": 130, "Kd": 24000}, - { "material": "Tool Steel", "ss_hss": 12, "ss_cbd": 45, "ss_drill_hss": 10, "ss_drill_cbd": 30, "kp": 1.88, "brinell": 400, "Kd": 340000 }, - { "material": "Stainless (303)", "ss_hss": 25, "ss_cbd": 85, "ss_drill_hss": 20, "ss_drill_cbd": 65, "kp": 2.07, "brinell": 200, "Kd": 200000 }, - { "material": "Stainless (304)", "ss_hss": 10, "ss_cbd": 37.5, "ss_drill_hss": 10, "ss_drill_cbd": 30, "kp": 2.07, "brinell": 125, "Kd": 22000 }, - { "material": "Stainless (316)", "ss_hss": 7.5, "ss_cbd": 25, "ss_drill_hss": 5, "ss_drill_cbd": 20, "kp": 2.07, "brinell": 80, "Kd": 24000 }, + {"material": "Softwood", "ss_hss": 225, "ss_cbd": 255, "ss_drill_hss": 185, "ss_drill_cbd": 205, "kp": 0.5, "brinell": 0, "Kd": 3000}, # noqa: E241 + {"material": "Hardwood", "ss_hss": 145, "ss_cbd": 275, "ss_drill_hss": 115, "ss_drill_cbd": 400, "kp": 0.75, "brinell": 0, "Kd": 4000}, # noqa: E241 + {"material": "Soft Plastics", "ss_hss": 225, "ss_cbd": 255, "ss_drill_hss": 185, "ss_drill_cbd": 205, "kp": 0.5, "brinell": 0, "Kd": 2000}, # noqa: E241 + {"material": "Hard Plastics", "ss_hss": 225, "ss_cbd": 275, "ss_drill_hss": 115, "ss_drill_cbd": 400, "kp": 0.75, "brinell": 0, "Kd": 2000}, # noqa: E241 + {"material": "Aluminium (6061)", "ss_hss": 175, "ss_cbd": 395, "ss_drill_hss": 135, "ss_drill_cbd": 310, "kp": 0.90, "brinell": 95, "Kd": 7000}, # noqa: E241 + {"material": "Aluminium (7075)", "ss_hss": 175, "ss_cbd": 395, "ss_drill_hss": 125, "ss_drill_cbd": 310, "kp": 0.90, "brinell": 150, "Kd": 7000}, # noqa: E241 + {"material": "Aluminium (Cast)", "ss_hss": 175, "ss_cbd": 395, "ss_drill_hss": 135, "ss_drill_cbd": 310, "kp": 0.68, "brinell": 150, "Kd": 7000}, # noqa: E241 + {"material": "Brass (Hard)", "ss_hss": 200, "ss_cbd": 395, "ss_drill_hss": 115, "ss_drill_cbd": 350, "kp": 2.27, "brinell": 120, "Kd": 14000}, # noqa: E241 + {"material": "Brass (Medium)", "ss_hss": 175, "ss_cbd": 350, "ss_drill_hss": 115, "ss_drill_cbd": 350, "kp": 1.36, "brinell": 120, "Kd": 14000}, # noqa: E241 + {"material": "Brass (Soft)", "ss_hss": 125, "ss_cbd": 300, "ss_drill_hss": 115, "ss_drill_cbd": 350, "kp": 0.68, "brinell": 120, "Kd": 7000}, # noqa: E241 + {"material": "Carbon Steel", "ss_hss": 35, "ss_cbd": 120, "ss_drill_hss": 25, "ss_drill_cbd": 90, "kp": 1.88, "brinell": 130, "Kd": 24000}, # noqa: E241 + {"material": "Tool Steel", "ss_hss": 12, "ss_cbd": 45, "ss_drill_hss": 10, "ss_drill_cbd": 30, "kp": 1.88, "brinell": 400, "Kd": 340000}, # noqa: E241 + {"material": "Stainless (303)", "ss_hss": 25, "ss_cbd": 85, "ss_drill_hss": 20, "ss_drill_cbd": 65, "kp": 2.07, "brinell": 200, "Kd": 200000}, # noqa: E241 + {"material": "Stainless (304)", "ss_hss": 10, "ss_cbd": 37.5, "ss_drill_hss": 10, "ss_drill_cbd": 30, "kp": 2.07, "brinell": 125, "Kd": 22000}, # noqa: E241 + {"material": "Stainless (316)", "ss_hss": 7.5, "ss_cbd": 25, "ss_drill_hss": 5, "ss_drill_cbd": 20, "kp": 2.07, "brinell": 80, "Kd": 24000}, # noqa: E241 ] return materials + def load_powerConstant(): powerConstant = { - ## Constant Power - ## Data from Machineries Handbook 28. - ## Table 2 - ## mm/tooth : C - 0.02: 1.70, - 0.05: 1.40, - 0.07: 1.30, - 0.10: 1.25, - 0.12: 1.20, - 0.15: 1.15, - 0.18: 1.11, - 0.20: 1.08, - 0.22: 1.06, - 0.25: 1.04, - 0.28: 1.01, - 0.30: 1.00, - 0.33: 0.98, - 0.35: 0.97, - 0.38: 0.95, - 0.40: 0.94, - 0.45: 0.92, - 0.50: 0.90, - 0.55: 0.88, - 0.60: 0.87, - 0.70: 0.84, - 0.75: 0.83, - 0.80: 0.82, - 0.90: 0.80, - 1.00: 0.78, - 1.50: 0.72 + # Constant Power + # Data from Machineries Handbook 28. + # Table 2 + # mm/tooth : C + 0.02: 1.70, # noqa: E131 + 0.05: 1.40, # noqa: E131 + 0.07: 1.30, # noqa: E131 + 0.10: 1.25, # noqa: E131 + 0.12: 1.20, # noqa: E131 + 0.15: 1.15, # noqa: E131 + 0.18: 1.11, # noqa: E131 + 0.20: 1.08, # noqa: E131 + 0.22: 1.06, # noqa: E131 + 0.25: 1.04, # noqa: E131 + 0.28: 1.01, # noqa: E131 + 0.30: 1.00, # noqa: E131 + 0.33: 0.98, # noqa: E131 + 0.35: 0.97, # noqa: E131 + 0.38: 0.95, # noqa: E131 + 0.40: 0.94, # noqa: E131 + 0.45: 0.92, # noqa: E131 + 0.50: 0.90, # noqa: E131 + 0.55: 0.88, # noqa: E131 + 0.60: 0.87, # noqa: E131 + 0.70: 0.84, # noqa: E131 + 0.75: 0.83, # noqa: E131 + 0.80: 0.82, # noqa: E131 + 0.90: 0.80, # noqa: E131 + 1.00: 0.78, # noqa: E131 + 1.50: 0.72 # noqa: E131 } - + return powerConstant + def load_feedFactor(): feedFactor = { - ## Feed Factor - ## Data from Machineries Handbook 28. - ## Table 33 - ## mm/rev : Ff - 0.01: 0.025, - 0.03: 0.060, - 0.05: 0.091, - 0.07: 0.133, - 0.10: 0.158, - 0.12: 0.183, - 0.15: 0.219, - 0.18: 0.254, - 0.20: 0.276, - 0.22: 0.298, - 0.25: 0.330, - 0.30: 0.382, - 0.35: 0.432, - 0.40: 0.480, - 0.45: 0.528, - 0.50: 0.574, - 0.55: 0.620, - 0.65: 0.780, - 0.75: 0.794, - 0.90: 0.919, - 1.00: 0.100, - 1.25: 1.195 - } - + # Feed Factor + # Data from Machineries Handbook 28. + # Table 33 + # mm/rev : Ff + 0.01: 0.025, # noqa: E131 + 0.03: 0.060, # noqa: E131 + 0.05: 0.091, # noqa: E131 + 0.07: 0.133, # noqa: E131 + 0.10: 0.158, # noqa: E131 + 0.12: 0.183, # noqa: E131 + 0.15: 0.219, # noqa: E131 + 0.18: 0.254, # noqa: E131 + 0.20: 0.276, # noqa: E131 + 0.22: 0.298, # noqa: E131 + 0.25: 0.330, # noqa: E131 + 0.30: 0.382, # noqa: E131 + 0.35: 0.432, # noqa: E131 + 0.40: 0.480, # noqa: E131 + 0.45: 0.528, # noqa: E131 + 0.50: 0.574, # noqa: E131 + 0.55: 0.620, # noqa: E131 + 0.65: 0.780, # noqa: E131 + 0.75: 0.794, # noqa: E131 + 0.90: 0.919, # noqa: E131 + 1.00: 0.100, # noqa: E131 + 1.25: 1.195 # noqa: E131 + } + return feedFactor + def load_diameterFactors(): - ## Diamater Factors - ## Data from Machineries Handbook 28. - ## Table 34 - ## mm, Fm + # Diamater Factors + # Data from Machineries Handbook 28. + # Table 34 + # mm, Fm diameterFactors = { - 1.60: 2.33, - 2.40: 4.84, - 3.20: 8.12, - 4.00: 12.12, - 4.80: 16.84, - 5.60: 22.22, - 6.40: 28.26, - 7.20: 34.93, - 8.00: 42.22, - 8.80: 50.13, - 9.50: 57.53, - 11.00: 74.90, - 12.50: 94.28, - 14.50: 123.1, - 16.00: 147.0, - 17.50: 172.8, - 19.00: 200.3, - 20.00: 219.7, - 22.00: 260.8, - 24.00: 305.1, - 25.50: 340.2, - 27.00: 377.1, - 28.50: 415.6, - 32.00: 512.0, - 35.00: 601.6, - 38.00: 697.6, - 42.00: 835.3, - 45.00: 945.8, - 48.00: 1062, - 50.00: 1143, - 58.00: 1493, - 64.00: 1783, - 70.00: 2095, - 76.00: 2429, - 90.00: 3293, - 100.00: 3981 + 1.60: 2.33, # noqa: E131 + 2.40: 4.84, # noqa: E131 + 3.20: 8.12, # noqa: E131 + 4.00: 12.12, # noqa: E131 + 4.80: 16.84, # noqa: E131 + 5.60: 22.22, # noqa: E131 + 6.40: 28.26, # noqa: E131 + 7.20: 34.93, # noqa: E131 + 8.00: 42.22, # noqa: E131 + 8.80: 50.13, # noqa: E131 + 9.50: 57.53, # noqa: E131 + 11.00: 74.90, # noqa: E131 + 12.50: 94.28, # noqa: E131 + 14.50: 123.1, # noqa: E131 + 16.00: 147.0, # noqa: E131 + 17.50: 172.8, # noqa: E131 + 19.00: 200.3, # noqa: E131 + 20.00: 219.7, # noqa: E131 + 22.00: 260.8, # noqa: E131 + 24.00: 305.1, # noqa: E131 + 25.50: 340.2, # noqa: E131 + 27.00: 377.1, # noqa: E131 + 28.50: 415.6, # noqa: E131 + 32.00: 512.0, # noqa: E131 + 35.00: 601.6, # noqa: E131 + 38.00: 697.6, # noqa: E131 + 42.00: 835.3, # noqa: E131 + 45.00: 945.8, # noqa: E131 + 48.00: 1062, # noqa: E131 + 50.00: 1143, # noqa: E131 + 58.00: 1493, # noqa: E131 + 64.00: 1783, # noqa: E131 + 70.00: 2095, # noqa: E131 + 76.00: 2429, # noqa: E131 + 90.00: 3293, # noqa: E131 + 100.00: 3981 # noqa: E131 } return diameterFactors + class Tool: def __init__(self, toolDia=6, flutes=3): self.toolDia = toolDia self.flutes = flutes + class Cnc_limits: - """ Define limits of your machines feed rate, rp, and power - + """ Define limits of your machines feed rate, rp, and power Defaults are low to help test over rides & also are for my low end home made CNC""" - - def __init__(self, feedMax=1000, rpmMin = 1000, rpmMax=12000, power=500): - + + def __init__(self, feedMax=1000, rpmMin=1000, rpmMax=12000, power=500): + self.cncFeedMax = feedMax # mm/min self.cncRpmMin = rpmMin self.cncRpmMax = rpmMax self.cncPower = power # in watts + class FSCalculation: def __init__(self): - self.opType = 'Milling' self.material = None self.rpm_overide = None self.ss_by_material = None @@ -212,131 +215,69 @@ def __init__(self): self.WOC = None self.DOC = None self.limits = None - - def calculate(self, tool): - print('calculate for opType: ', self.opType) + def get_surface_speed(self): + print("material", self.material) + if self.material: + materials = load_materials() + surfaceSpeed = next(item for item in materials if item["material"] == self.material).get(self.ss_by_material) + print("found ss:", surfaceSpeed) + return surfaceSpeed + + return "-" + + def set_material(self, material): + self.material = material + + def calculate(self, tool, surfaceSpeed): + materials = load_materials() - surfaceSpeed = next(item for item in materials if item["material"] == self.material).get(self.ss_by_material) + # surfaceSpeed = self.get_surface_speed() Kp = next(item for item in materials if item["material"] == self.material).get("kp") - ## C = Power Constant - C = getInterpolatedValue(load_powerConstant(), self.feedPerTooth) + # C = Power Constant + C = getInterpolatedValue(load_powerConstant(), self.feedPerTooth) rpm = int((1000 * surfaceSpeed) / (math.pi * tool.toolDia)) calc_rpm = rpm - + if self.rpm_overide: calc_rpm = float(self.rpm_overide) - ## Machine Efficiency: Pg 1049 - E = 0.80 - - ##### Machining Power ##### - if self.opType == 'Milling': - feed = int(calc_rpm * self.feedPerTooth * tool.flutes) - ## Calculation to Machineries Handbook: Pg 1058 - #print("WOC", self.WOC, " DOC", self.DOC, " Feed", feed) - ## Material Removal Rate: Pg 1049 - Q = float((self.WOC * self.DOC * feed) / 60000) ##cm^3/s - #print("Kp", Kp, " C", C, " Q", round(Q * 60, 2), " W", self.toolWear, " E", E) - ## Power Required at the cutter: Pg 1048 - Pc = Kp * C * Q * self.toolWear - - if self.opType == 'Drilling': - feed = int(self.feedPerTooth * calc_rpm) - # Kd = Work material factor (Table 31) - # Ff = Feed factor (Table 33) - # FM = Torque factor for drill diameter (Table 34) - # A = Chisel edge factor for torque (Table 32) - # w = Web thickness at drill point (See Table 32) - - Kd = next(item for item in materials if item["material"] == self.material).get("Kd") - Ff = getInterpolatedValue(load_feedFactor(), self.feedPerTooth) - Fm = getInterpolatedValue(load_diameterFactors(), tool.toolDia) - A = 1.085 #fixed value based on standard 118 deg drill. Table 32 - W = 0.18 * tool.toolDia #fixed value based on standard 118 deg drill. Table 32 - - M = Kd * Ff * Fm * A * W / 40000 #pg 1054 - Pc = M * calc_rpm / 9550 #pg 1054 - - - ## Power Required at the motor + # Machine Efficiency: Pg 1049 + E = 0.80 + + # Machining Power + # Horizontal Feed + hfeed = int(calc_rpm * self.feedPerTooth * tool.flutes) + # Calculation to Machineries Handbook: Pg 1058 + # print("WOC", self.WOC, " DOC", self.DOC, " Feed", feed) + # Material Removal Rate: Pg 1049 + Q = float((self.WOC * self.DOC * hfeed) / 60000) # cm^3/s + # print("Kp", Kp, " C", C, " Q", round(Q * 60, 2), " W", self.toolWear, " E", E) + # Power Required at the cutter: Pg 1048 + Pc = Kp * C * Q * self.toolWear + + # Vertical Feed + vfeed = int(self.feedPerTooth * calc_rpm) + # Kd = Work material factor (Table 31) + # Ff = Feed factor (Table 33) + # FM = Torque factor for drill diameter (Table 34) + # A = Chisel edge factor for torque (Table 32) + # w = Web thickness at drill point (See Table 32) + + # TODO: Re-enable drilling power + # drilling power: + # Kd = next(item for item in materials if item["material"] == self.material).get("Kd") + # Ff = getInterpolatedValue(load_feedFactor(), self.feedPerTooth) + # Fm = getInterpolatedValue(load_diameterFactors(), tool.toolDia) + # A = 1.085 # fixed value based on standard 118 deg drill. Table 32 + # W = 0.18 * tool.toolDia # fixed value based on standard 118 deg drill. Table 32 + + # M = Kd * Ff * Fm * A * W / 40000 # pg 1054 + # Pc = M * calc_rpm / 9550 # pg 1054 + + # Power Required at the motor Pm = Pc / E # Convert to Hp Hp = Pm * 1.341 - #print("power", Pc, Pm, Hp) - return calc_rpm, feed, Hp - - -# TODO change tool & limits to be part of FSCalculation & no need pass around?? - def printInputs(self, tool, limits): - print("\n\t\t -- Display Inputs to the 'Feed and Speed Calculator'") - print("\tCNC limits:- FeedMax: %d, RpmMin: %d, RpmMax: %d, PowerMax: %d" % (limits.cncFeedMax, limits.cncRpmMin, limits.cncRpmMax, limits.cncPower)) - print("\ttool:- Dia: %.2f, flutes: %d, material: %s" % (tool.toolDia, tool.flutes, self.ss_by_material)) - print("\tmaterial: %s" % self.material) - print("\topType: %s, toolWear: %.2f" % (self.opType, self.toolWear)) - print("\tfeedPerTooth: %.3f, WOC: %.2f, DOC: %.2f" % (self.feedPerTooth, self.WOC, self.DOC)) - if self.opType == False : - print('\tNote: Use Peck drilling if hole depth >4x tool dia, ie ' + str(tool.toolDia * 4)) - print(" --- Display Inputs end\n") - - def initDefaults(self): - - # Set the limits of your CNC Mill. You can pass in your own values to over ride the defaults. - limits = Cnc_limits() #Defaults: feedMax=1000, rpmMin = 1000, rpmMax=12000, power=500 - # fs = PathFeedsAndSpeeds.FSCalculation() - - # Set params of the tool. Note, tool material is set below for either Milling or Drilling. - tool = Tool() - tool.toolDia = float(6.0) - tool.flutes = 2 - - self.opType = 'Milling' # operation is Milling or Drilling - # Material to be cut. Find exact name from above # REFERENCE: Materials list. - self.material = "Hard Plastics" #"Hardwood" # "Aluminium (6061)" - - # Milling or Drilling specific params. - # Note you can for set parameters to be dependant upon others, - # for eg: DOC to be a fraction of your Tool dia, eg: self.DOC = tool.toolDia/2 - if self.opType == 'Milling' : - self.feedPerTooth = float(0.060) - self.WOC = 1.2 - self.DOC = 6 - self.toolWear = 1.1 ## Tool Wear pg: 1048 - self.ss_by_material = "ss_cbd" # "ss_hss" "ss_cbd" - else: - self.feedPerTooth = float(0.060) - self.toolWear = 1.3 ## Tool Wear pg: 1048 - self.ss_by_material = "ss_drill_hss" # "ss_drill_hss' "ss_drill_cbd" - if tool.toolDia: - print('Note: Use Peck drilling if hole depth >4x tool dia, ie ' + str(tool.toolDia * 4)) - return tool, limits - - def calcWithAutoOverrides(self, tool, limits): - - rpm, feed, Hp = self.calculate(tool) - print("\trpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, (Hp*745.6999))) - if rpm > limits.cncRpmMax: - self.rpm_overide = limits.cncRpmMax - rpm, feed, Hp = self.calculate(tool) - print("\t >>>OVERRIDING RPM to cnc max rpm") - print("\t rpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, Hp*745.6999)) - - if rpm < limits.cncRpmMin: - self.rpm_overide = limits.cncRpmMin - rpm, feed, Hp = self.calculate(tool) - print("\t >>>OVERRIDING RPM to cnc *MIN* rpm") - print("\t rpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, Hp*745.6999)) - - if feed > limits.cncFeedMax: - self.rpm_overide = rpm * limits.cncFeedMax/feed - rpm, feed, Hp = self.calculate(tool) - print("\t >>>OVERRIDING & reducing FEED ") - print("\t rpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, Hp*745.6999)) - - if Hp*745.6999 > limits.cncPower: - self.rpm_overide = feed * limits.cncPower/Hp*745.6999 - rpm, feed, Hp = self.calculate(tool) - print("\t >>>OVERRIDING Power required & reducing FEED ") - print("\t rpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, Hp*745.6999)) - return (rpm, feed, Hp) - + # print("power", Pc, Pm, Hp) + return calc_rpm, hfeed, vfeed, Hp diff --git a/PathFeedsAndSpeedsGui.py b/PathFeedsAndSpeedsGui.py index 039c239..37e5abe 100644 --- a/PathFeedsAndSpeedsGui.py +++ b/PathFeedsAndSpeedsGui.py @@ -1,130 +1,162 @@ # Feed and Speed Calculator # Provides a basic feeds and speeds calculator for use with FreeCAD Path -import FreeCAD,FreeCADGui +import FreeCAD, FreeCADGui import os -from PySide import QtGui, QtCore -from PySide.QtGui import QApplication, QDialog, QMainWindow +from PySide import QtGui import PathFeedsAndSpeeds dir = os.path.dirname(__file__) ui_name = "PathFeedsAndSpeedsGui.ui" -path_to_ui = dir + "/" +ui_name +path_to_ui = dir + "/" + ui_name + class FeedSpeedPanel(): def __init__(self): - #Build GUI + # Build GUI self.form = FreeCADGui.PySideUic.loadUi(path_to_ui) - self.tabWidget = self.form.tabWidget - self.material_CB = self.form.material_CB - self.hss_RB = self.form.hss_RB - self.cbd_RB = self.form.cbd_RB - ##################################### - ## Milling Page ## - ##################################### - self.toolDia_LE = self.form.toolDia_LE - self.flutes_SB = self.form.flutes_SB - self.FPT_SB = self.form.FPT_SB - self.WOC_SP = self.form.WOC_SP - self.DOC_SP = self.form.DOC_SP - ##################################### - ## Drilling Page ## - ##################################### - self.drillDia_LE = self.form.drillDia_LE - self.drillFPT_SB = self.form.drillFPT_SB - self.drilling_notes = self.form.drilling_notes - ##################################### - ## Results ## - ##################################### - self.rpm_result = self.form.rpm_result - self.rpm_LE = self.form.rpm_LE - self.feed_result= self.form.feed_result - self.hp_result = self.form.hp_result - ### Set Defaults + + # Set Defaults self.toolDia = 6 - ### Init + + # Init + self.calculation = PathFeedsAndSpeeds.FSCalculation() self.setup_ui() self.calculate() - ### connect - self.tabWidget.currentChanged.connect(self.calculate) - self.material_CB.currentIndexChanged.connect(self.calculate) - self.hss_RB.toggled.connect(self.calculate) - self.cbd_RB.toggled.connect(self.calculate) - self.toolDia_LE.textChanged.connect(self.calculate) - self.flutes_SB.valueChanged.connect(self.calculate) - self.FPT_SB.valueChanged.connect(self.calculate) - self.WOC_SP.textChanged.connect(self.calculate) - self.DOC_SP.textChanged.connect(self.calculate) - self.rpm_LE.textChanged.connect(self.calculate) - self.drillDia_LE.textChanged.connect(self.calculate) - self.drillFPT_SB.valueChanged.connect(self.calculate) - - def setup_ui(self): - - ### load materials - materials = PathFeedsAndSpeeds.load_materials() + # connect + self.form.material_CB.currentIndexChanged.connect(self.set_material) + self.form.hss_RB.toggled.connect(self.calculate) + self.form.cbd_RB.toggled.connect(self.calculate) + self.form.toolDia_LE.textChanged.connect(self.calculate) + self.form.flutes_SB.valueChanged.connect(self.calculate) + self.form.FPT_SB.valueChanged.connect(self.calculate) + self.form.WOC_SP.textChanged.connect(self.calculate) + self.form.DOC_SP.textChanged.connect(self.calculate) + self.form.ss_LE.textChanged.connect(self.calculate) + self.form.rpm_LE.textChanged.connect(self.calculate) + self.form.toolController_CB.currentIndexChanged.connect(self.load_tool_properties) + self.form.update_PB.clicked.connect(self.update_tool_controller) + self.form.close_PB.clicked.connect(self.quit) + + def setup_ui(self): + # load materials + # materials = PathFeedsAndSpeeds.load_materials() for material in (d['material'] for d in PathFeedsAndSpeeds.load_materials()): - self.material_CB.addItem(material) + self.form.material_CB.addItem(material) + + # load widget data + self.set_tool_properties(self.toolDia, 2, self.toolDia * 0.01, "HSS") + + # set input validation + self.onlyInt = QtGui.QIntValidator() + self.form.ss_LE.setValidator(self.onlyInt) + self.form.rpm_LE.setValidator(self.onlyInt) + + self.load_tools() + self.load_tool_properties() + self.set_tool_material() + self.set_material() + + def set_tool_properties(self, dia, flutes, chipload, material): + self.form.toolDia_LE.setText(str(dia)) + self.form.flutes_SB.setValue(flutes) + self.form.FPT_SB.setValue(chipload) + + self.form.WOC_SP.setText(str(round(dia * 0.2, 2))) + self.form.DOC_SP.setText(str(dia)) + + if material == "HSS": + self.form.hss_RB.setChecked(True) + elif material == "Carbide": + self.form.cbd_RB.setChecked(True) + + def set_material(self): + material = self.form.material_CB.currentText() + self.calculation.set_material(material) + ss = self.calculation.get_surface_speed() + self.form.ss_LE.setText(str(ss)) + self.calculate + + def set_tool_material(self): + self.calculation.ss_by_material = "ss_hss" if self.form.hss_RB.isChecked() else "ss_cbd" + + def load_tools(self): + jobs = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") + # self.form.toolController_CB.addItem('None') + for job in jobs: + for idx, tc in enumerate(job.Tools.Group): + self.form.toolController_CB.addItem(tc.Label) + + def load_tool_properties(self): + tc = self.get_tool_controller() + + if tc: + tool = tc.Tool + dia = tool.Diameter + flutes = tool.Flutes + material = tool.Material + chipload = tool.Chipload + self.set_tool_properties(dia, flutes, chipload, material) + print("tool props:", dia, flutes, material, chipload) + + def get_tool_controller(self): + jobs = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") + tcStr = self.form.toolController_CB.currentText() + for job in jobs: + for tc in job.Tools.Group: + if tc.Label == tcStr: + return tc + + return None + + def update_tool_controller(self): + tc = self.get_tool_controller() + + if tc: + rpm = self.form.rpm_result.text() + hfeed = self.form.hfeed_result.text() + vfeed = self.form.vfeed_result.text() + # TODO: Add a confirmation dialog + tc.HorizFeed = hfeed + tc.VertFeed = vfeed + tc.SpindleSpeed = float(rpm) - ### set tool material - self.hss_RB.setChecked(True) + def calculate(self): - ### load widget data - self.toolDia_LE.setText(str(self.toolDia)) - self.flutes_SB.setValue(2) - self.FPT_SB.setValue(self.toolDia * 0.01) - self.WOC_SP.setText(str(round(self.toolDia * 0.2, 2))) - self.DOC_SP.setText(str(self.toolDia)) - self.drillDia_LE.setText(str(self.toolDia)) - self.drillFPT_SB.setValue(self.toolDia * 0.01) + if self.calculation.material is None: + return - def calculate(self): tool = PathFeedsAndSpeeds.Tool() - calculation = PathFeedsAndSpeeds.FSCalculation() - calculation.material = self.material_CB.currentText() - calculation.rpm_overide = self.rpm_LE.text() - if not self.rpm_LE.text() == "": - self.rpm_result.setEnabled(False) - else: - self.rpm_result.setEnabled(True) - - if self.tabWidget.currentIndex() == 0: - # calculate for milling - tool.toolDia = float(self.toolDia_LE.text()) - tool.flutes = self.flutes_SB.value() - calculation.feedPerTooth = float(self.FPT_SB.value()) - calculation.WOC = FreeCAD.Units.Quantity(self.WOC_SP.text()) - calculation.DOC = FreeCAD.Units.Quantity(self.DOC_SP.text()) - calculation.toolWear = 1.1 ## Tool Wear pg: 1048 - calculation.ss_by_material = "ss_hss" if self.hss_RB.isChecked() else "ss_cbd" - calculation.opType = 'Milling' + self.calculation.rpm_overide = self.form.rpm_LE.text() + surfaceSpeed = float(self.form.ss_LE.text()) + + if not self.form.rpm_LE.text() == "": + self.form.rpm_result.setEnabled(False) else: - # calculate for drilling - tool.toolDia = float(self.drillDia_LE.text()) - calculation.feedPerTooth = float(self.drillFPT_SB.value()) - calculation.toolWear = 1.3 ## Tool Wear pg: 1048 - calculation.ss_by_material = "ss_drill_hss" if self.hss_RB.isChecked() else "ss_drill_cbd" - calculation.opType = 'Drilling' - print('tooldia', tool.toolDia) - if tool.toolDia: - notes = 'Note: Peck drilling should be used when hole depth > {0}' - self.drilling_notes.setText(notes.format(tool.toolDia * 4)) - else: - self.drilling_notes.setText('') - - if not calculation.feedPerTooth: + self.form.rpm_result.setEnabled(True) + + tool.toolDia = FreeCAD.Units.Quantity(self.form.toolDia_LE.text()) + tool.flutes = self.form.flutes_SB.value() + self.calculation.feedPerTooth = float(self.form.FPT_SB.value()) + self.calculation.WOC = FreeCAD.Units.Quantity(self.form.WOC_SP.text()) + self.calculation.DOC = FreeCAD.Units.Quantity(self.form.DOC_SP.text()) + self.calculation.toolWear = 1.1 # Tool Wear pg: 1048 + self.set_tool_material() + + if not self.calculation.feedPerTooth: return - rpm, feed, Hp = calculation.calculate(tool) + rpm, hfeed, vfeed, Hp = self.calculation.calculate(tool, surfaceSpeed) + watts = Hp * 745.69 - self.rpm_result.setText(str(rpm)) - self.feed_result.setText(str(feed) + " mm/min") - self.hp_result.setText(str(round(Hp, 2)) + " hp") + self.form.rpm_result.setText(str(rpm)) + self.form.hfeed_result.setText(str(hfeed) + " mm/min") + self.form.vfeed_result.setText(str(vfeed) + " mm/min") + self.form.hp_result.setText(str(round(Hp, 2)) + " hp / " + str(round(watts, 2)) + " watts") def show(self): - self.form.show() self.form.exec_() def reject(self): @@ -133,19 +165,23 @@ def reject(self): def accept(self): self.quit() - + def quit(self): - FreeCADGui.Control.closeDialog(self) + self.form.close() def reset(self): pass -def Show(): - panel = FeedSpeedPanel() - panel.show() - - - +def Show(): + if not FreeCAD.ActiveDocument: + QtGui.QMessageBox.warning(FreeCADGui.getMainWindow(), "Warning", "No Active Document") + return + jobs = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") + if len(jobs) == 0: + QtGui.QMessageBox.warning(FreeCADGui.getMainWindow(), "Warning", "No Path Job in Current Document") + return + panel = FeedSpeedPanel() + panel.show() diff --git a/PathFeedsAndSpeedsGui.ui b/PathFeedsAndSpeedsGui.ui index fc0f079..e93aadb 100644 --- a/PathFeedsAndSpeedsGui.ui +++ b/PathFeedsAndSpeedsGui.ui @@ -7,7 +7,7 @@ 0 0 350 - 465 + 643 @@ -28,61 +28,115 @@ - + - - - 12 + + + Tool - - 0 - - - 12 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 75 - 16777215 - - - - Material: - - - - - - - - - - - - QFrame::Box - - - QFrame::Raised - - + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Tool Controller: + + + + + + + <html><head/><body><p>Tool Controller</p></body></html> + + + - + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Tool Diameter: + + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Flutes: + + + + + + + <html><head/><body><p>Number of flutes or cutting edges</p></body></html> + + + false + + + 1 + + + + + + + 0 + - + 0 @@ -91,53 +145,357 @@ - 0 + 125 0 - 35 + 16777215 16777215 - Feed: + Material: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + <html><head/><body><p>High Speed Steel</p></body></html> + + + HSS - + + + <html><head/><body><p>carbide</p></body></html> + - - + Carbide + + + + + 0 + 0 + + + + <html><head/><body><p>Tool Diameter</p></body></html> + + + true + + + + + + + + + + + + + Feed + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Chipload: + + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Stepover: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>stepover or width of cut</p></body></html> + + + + + + + + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Stepdown: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>stepdown or depth of cut</p></body></html> + + + + + + - + + + <html><head/><body><p>chipload or feed per tooth. </p></body></html> + + + 3 + + + 0.020000000000000 + + + 1.500000000000000 + + + 0.010000000000000 + + + 0.020000000000000 + + + + + + + + + + Speed + + + + + + + 0 + 0 + + + + + 125 + 0 + + - 150 + 16777215 16777215 - - Qt::LeftToRight + + Material: + + + + + + + <html><head/><body><p>material of stock</p></body></html> + + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Surface Speed: - Qt::AlignCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Spindle Speed: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + <html><head/><body><p>surface speed</p></body></html> - RPM Overide + Overide + + + + + + + + 0 + 0 + + + + <html><head/><body><p>spindle speed or rpm</p></body></html> + + + Overide + + + true + + + + + + + + + + + + + Output + + - + - + 0 @@ -146,23 +504,59 @@ - 0 + 125 0 - 35 + 16777215 16777215 - RPM: + Horizontal Feed: - + + + - + + + + + + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Vertical Feed: + + + + + - @@ -182,13 +576,13 @@ - 0 + 125 0 - 35 + 16777215 16777215 @@ -206,393 +600,122 @@ + + + + + + + 0 + 0 + + + + + 125 + 0 + + + + + 16777215 + 16777215 + + + + Spindle Speed: + + + + + + + - + + + + + - - - - - 0 - 0 - + + + + Qt::Vertical - + - 0 - 0 + 20 + 2 - - 0 - - - - Milling - - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - - 16777215 - 16777215 - - - - DOC: - - - - - - - - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - - 16777215 - 16777215 - - - - FPT: - - - - - - - 3 - - - 0.020000000000000 - - - 1.500000000000000 - - - 0.010000000000000 - - - - - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - - 16777215 - 16777215 - - - - No. Flutes: - - - - - - - - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - - 16777215 - 16777215 - - - - WOC: - - - - - - - false - - - - - - - - - 6 - - - - - - 0 - 0 - - - - - 75 - 0 - - - - - 16777215 - 16777215 - - - - Tool Diameter: - - - - - - - - - - - - Qt::Vertical + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <html><head/><body><p>apply the feed and spindle speed to the selected tool controller</p></body></html> - - - 20 - 40 - + + Update TC - + - - - - - Drilling - - - - - - Qt::Vertical - - - - 20 - 40 - + + + + <html><head/><body><p>close the dialog</p></body></html> - - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - - 75 - 16777215 - - - - FPR: - - - - - - - 3 - - - 0.020000000000000 - - - 1.250000000000000 - - - 0.010000000000000 - - - - - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - - 75 - 16777215 - - - - Drill Diameter: - - - - - - - - - - - - - - - Qt::AlignCenter - - - true + Close - - - - - - - 12 - - - - - - 0 - 0 - - - - - 75 - 16777215 - - - - Tool: - - - - - - - HSS - - - - - - - Carbide - - + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
- material_CB - hss_RB - cbd_RB - tabWidget + toolController_CB toolDia_LE flutes_SB + hss_RB + cbd_RB FPT_SB WOC_SP DOC_SP + material_CB + ss_LE rpm_LE - drillDia_LE - drillFPT_SB + update_PB + close_PB diff --git a/README.md b/README.md index 358217a..28e088b 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,14 @@ This addon when installed will appear in the Path Workbench toolbar menu, the pu for the spindle speed and feedrate. This can be done by Spindle RPM or chipload (Feed Per Tooth). ## Features +* Select path tool controller * Select workpiece material * Select Tool Material * Set machining parameters -* Scripting only features (not yet added to GUI) - * Ability to automaticaly over ride outputs to limits set to match your CNC. - * Optional print of all inputs - * Script example included in examples directory +* write calculated feedrate and spindle speed to the selected tool controller ## Requirements -* FreeCAD v0.19 +* FreeCAD v0.19 or FreeCAD v0.20 * Python3 * Qt5 diff --git a/examples/scripted_creation_Tool_Controllers.FCMacro b/examples/scripted_creation_Tool_Controllers.FCMacro deleted file mode 100644 index bf584d3..0000000 --- a/examples/scripted_creation_Tool_Controllers.FCMacro +++ /dev/null @@ -1,394 +0,0 @@ -# Demonstration script to run an extended version of the Feed and Speed Calculator FreeCAD addon, -# which can be installed from the "rpm_override" branch at https://github.com/spanner888/FeedsAndSpeeds/tree/rpm_override -# Original code is https://github.com/dubstar-04/FeedsAndSpeeds - -# This scripts demonstrates original Feed and Speed Calculator capability -# and also the added over ride limits set to match your CNC. - -# version 1.0 - - -# My Tool Library stuffed - have *some* c'drills & drills crosslinked - ie edite one changes the other...so never correct! -# ..prob was me hand editing lib.... -# So now want to automate Tool Lib creation, but seems like can't easily automate PathToolLibrary's due to gui code popups, -# so now decided just to create required tools directly in a PathJob -# and of course the associated ToolController for each tool with Feeds & Speeds etc.... -# based heavily on https://forum.freecadweb.org/viewtopic.php?f=15&t=53849 -# Future integrate with a Feeds and speeds calculator, such as - # dubstar-04 - # feeds and speeds add-on for the path workbench https://github.com/dubstar-04/FeedsAndSpeeds - -# ATM allows some simple auto changing of Feeds, Speeds for each tool in a range. -# ATM Lotsa poor code, - # eg if tool ype/shape file spelt wrong, you get adn exception with no helpfull info. - # No Tool resource icons added. - - -# TODO test shapefile case sensistive names on linux...including fcstd or FCStd - -# ...or look/TEST IF CAN AUTOMATE WITH russ's changes -# FYI: russ422 pathscript automation changes include (ie prob other key changes) def Create(base, template=None, useGui=True): - -import FreeCAD -from PySide import QtGui -from FreeCAD import Base, Rotation, Vector -import Path - -import PathFeedsAndSpeeds - -# from PathScripts import PathToolBitLibraryCmd -# Gui.runCommand('Path_ToolBitLibraryOpen',0) -from PathScripts import PathJob, PathJobGui -from PathScripts import PathPreferences, PathPreferencesAdvanced, PathPreferencesPathDressup, PathPreferencesPathJob, PathPost -from PathScripts import PathToolBit, PathToolController - - -# helper stuff -def initdoc(DOC_NAME): - DOC = FreeCAD.activeDocument() - if DOC is None: - FreeCAD.newDocument(DOC_NAME) - FreeCAD.setActiveDocument(DOC_NAME) - DOC = FreeCAD.activeDocument() - else: - clear_doc(DOC) - - # clear the report window https://forum.freecadweb.org/viewtopic.php?t=9825 - mw=FreeCAD.Gui.getMainWindow() - - c=mw.findChild(QtGui.QPlainTextEdit, "Python console") - c.clear() - - r=mw.findChild(QtGui.QTextEdit, "Report view") - r.clear() - - return (DOC) - -def clear_doc(DOC): - """ - Clear the active document deleting all the objects - """ - for obj in DOC.Objects: - DOC.removeObject(obj.Name) - -def setview(DOC, VIEW): - """Rearrange View""" - DOC.recompute() - VIEW.viewAxometric() - VIEW.setAxisCross(True) - VIEW.fitAll() - -def makeCyl(DOC, cyl_r = 2, cyl_h = 20, cyl_angle = 360, - place_x = 0, place_y = 0, place_z = 0, - rot_axis_x = 0, rot_axis_y = 0, rot_axis_z = 0, rot_angle = 0): - cyl = DOC.addObject('Part::Cylinder', 'oneCylinder') - cyl.Radius = cyl_r - cyl.Height = cyl_h - cyl.Angle = cyl_angle - cyl.Placement = FreeCAD.Placement( - FreeCAD.Vector(place_x, place_y, place_z), - FreeCAD.Rotation(FreeCAD.Vector(rot_axis_x, rot_axis_y, rot_axis_z), rot_angle)) - return (cyl) - -def makeCube(DOC, rect_x = 10, rect_y = 20, rect_z = 3, - place_x = 0, place_y = 0, place_z = 0, - rot_axis_x = 0, rot_axis_y = 0, rot_axis_z = 0, rot_angle = 0): - - box = DOC.addObject('Part::Box', 'Box') - box.Width = rect_x - box.Length = rect_y - box.Height = rect_z - box.Placement = FreeCAD.Placement( - FreeCAD.Vector(place_x, place_y, place_z), - FreeCAD.Rotation(FreeCAD.Vector(rot_axis_x, rot_axis_y, rot_axis_z), rot_angle)) - return box - - -# Now functions related to Tools & ToolControllers -def initJob(jobName, thisShape, template = None): - # job = PathJob.Create(jobName, [thisShape], template) - job = PathJob.Create('Job', [thisShape], template) - # job = PathJobGui.Create([thisShape], template) - job.ViewObject.Proxy = PathJobGui.ViewProvider(job.ViewObject) - job.Label = jobName - - return (job) - -# Based on FreeCAD TestPathToolController.py -def createTool(name='t1', diameter=1.75, shape=None): - if PathPreferences.toolsUseLegacyTools(): - return Path.Tool(name=name, diameter=diameter) - # attrs = {'shape': None, 'name': name, 'parameter': {'Diameter': diameter}, 'attribute': []} - attrs = {'shape': pathPrefs.GetString("LastPathToolShape") + '/' + shape + '.fcstd', 'name': name, 'parameter': {'Diameter': diameter}, 'attribute': []} - # tool = PathToolBit.Factory.CreateFromAttrs(attrs, name) - # toolShapePath = pathPrefs.GetString("LastPathToolShape") + '/' + shape + '.fcstd' - # print(toolShapePath) - # tool.BitShape = toolShapePath - return PathToolBit.Factory.CreateFromAttrs(attrs, name) - -# Based on FreeCAD PathJob: -def addToolController(job, tc): #<<: 'NoneType' object has no attribute 'startswith' -# above error while trying to open edit panel?? -def createRangeToolControllers(toolNumberStart, toolCount, toolShape, - diaStart, diaInc, - vRapid, vFeedStart, vFeedInc, - hRapid, hFeedStart, hFeedInc, - spRpmStart, spRpmInc, spDir ='Forward'): - """ Create & add to the Job a range of ToolController (with rapids, feeds & speeds) & assoc ToolBits & properties - - Tool diameters, feeds and speed are incremented as specified for each tool. - In general increasing tool diameter would require decreasing feeds or speeds or vice verse. - So use positive increment for Tool dia and negative for feeds & speeds or vice versa.""" - - for toolIndex in range (toolCount): - - # create Tool start dia = 2.5, incrementing 1.5mmm for example - toolDia = diaStart + toolIndex*diaInc - # Naming this way to avoid trailing numbers being auto renamed by FC - t = createTool("T%04d_%s" %(100*toolDia, toolShape), toolDia, toolShape) - print("created toolbit: T%04d_%s" %(100*toolDia, toolShape)) - - # Now for ToolController - tc = PathToolController.Create("TC%02d_" % toolIndex, t) - # tcProps = PathToolController.ToolControllerTemplate - tc.Label = "TC%04d_%s" %(100*toolDia, toolShape) - tc.ToolNumber = toolNumberStart + toolIndex - - tc.VertRapid = vRapid/60 - # tc.VertRapid = round(tc.VertRapid, 2) - - tc.VertFeed = (vFeedStart + toolIndex * vFeedInc)/60 - # tc.VertFeed = round(tc.VertFeed, 2) - - tc.HorizRapid = hRapid/60 - # tc.HorizRapid = round(tc.HorizRapid, 2) - - tc.HorizFeed = (hFeedStart + toolIndex * hFeedInc)/60 - # tc.HorizFeed = round(tc.HorizFeed, 2) - - tc.SpindleSpeed = spRpmStart + toolIndex * spRpmInc - tc.SpindleDir = spDir - addToolController(job, tc) - -def createRangeTcUsingFsCalc(toolNumberStart, toolCount, toolShape, - diaStart, diaInc, - vRapid, vFeedStart, vFeedInc, - hRapid, spDir ='Forward'): - """ Create & add to the Job a range of ToolController (with rapids, feeds & speeds) & assoc ToolBits & properties - - Tool diameters, feeds and speed are incremented as specified for each tool. - In general increasing tool diameter would require decreasing feeds or speeds or vice verse. - So use positive increment for Tool dia and negative for feeds & speeds or vice versa.""" - - for toolIndex in range (toolCount): - - # use Feed and Speed Calculator FreeCAD addon to calc updated rpm & feed - # Now Change defaults to suit your needs. See initFsDefaults() for details. - # Some examples:- - tool.toolDia = diaStart + toolIndex*diaInc - tool.flutes = 1 - fsAddon.material = "Aluminium (6061)" - - # Optional print of all the inputs - # fsAddon.printInputs(tool, limits) - - # Original calculator behaviour, no SCRIPTED over rides - # rpm, feed, Hp = fsAddon.calculate(tool) - # print("\t rpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, Hp*745.6999)) - - # auto override feed, rpm & power, based on cnc limit settings - fsAddon.printInputs(tool, limits) - rpm, feed, Hp = fsAddon.calcWithAutoOverrides(tool, limits) - # print("\t rpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, Hp*745.6999)) - - - # Naming this way to avoid trailing numbers being auto renamed by FC - t = createTool("T%04d_%s" %(100*tool.toolDia, toolShape), tool.toolDia, toolShape) - # TODO add number tool flutes & anything else??? - print("created toolbit: T%04d_%s" %(100*tool.toolDia, toolShape)) - - # Now for ToolController - tc = PathToolController.Create("TC%02d_" % toolIndex, t) - # tcProps = PathToolController.ToolControllerTemplate - tc.Label = "TC_FS_%04d_%s" %(100*tool.toolDia, toolShape) - tc.ToolNumber = toolNumberStart + toolIndex - - tc.VertRapid = vRapid/60 - # tc.VertRapid = round(tc.VertRapid, 2) - - tc.VertFeed = (vFeedStart + toolIndex * vFeedInc)/60 - # tc.VertFeed = round(tc.VertFeed, 2) - - tc.HorizRapid = hRapid/60 - # tc.HorizRapid = round(tc.HorizRapid, 2) - - # feed from Feed and Speed Calculator FreeCAD addon, - tc.HorizFeed = feed/60 # divide by 60 for mm/min vs mm/s - # tc.HorizFeed = round(tc.HorizFeed, 2) - - # SpindleSpeed from Feed and Speed Calculator FreeCAD addon - tc.SpindleSpeed = rpm - tc.SpindleDir = spDir - - jobTC = addToolController(job, tc) -# ------------------ setup stuff ----------------------------------- -DOC_NAME = "scripted_creation_Tool_Controllers" -DOC = initdoc(DOC_NAME) -VIEW = FreeCAD.Gui.ActiveDocument.ActiveView - -# init of PathFeedsAndSpeeds calculator -fsAddon = PathFeedsAndSpeeds.FSCalculation() -# Easy set defaults, situation specific then set in calls to createRangeToolControllers -tool, limits = fsAddon.initDefaults() - -# create solid for stock/model & place it as desired -stock1 = makeCube(DOC, 2*156.5, 2*155, 19) -stock1.Placement.Base = Vector(-156.5, -155, 0) -# Job to add ToolController to... -job = initJob("tlaJob1", stock1, None) - -# Get path to Tool Shape files -pathPrefs=FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path") -print("LastPathToolBit %s" % pathPrefs.GetString("LastPathToolBit")) -toolspath = pathPrefs.GetString("LastPathToolBit") -print("LastPathToolShape %s" % pathPrefs.GetString("LastPathToolShape")) -print('--------------') -# ----------------- end setup --------------------------------------- - -# NB feed/rapids are mm/min -# tool shapes are case sensitive, as are spindle directions -createRangeToolControllers(5, 4, 'endmill', 3.0, 1.5, 200, 100, -10, - 800, 400, -50, - 12000, 0, 'Reverse') -createRangeTcUsingFsCalc(5, 4, 'endmill', - 3.0, 1.5, - 200, 100, -10, - 800, 'Reverse') -# createRangeToolControllers(useFsCalc, 15, 2, 'drill', 4.0, 1.0, 200, 100, -10, - # 800, 400, -50, - # 12000, 0, 'Forward') -# createRangeToolControllers(useFsCalc, 25, 3, 'centredrill', 2.0, 1.1, 200, 100, -10, - # 800, 400, -50, - # 12000, 0, 'Forward') - -# endmill -# drill -# centredrill <<>>NB: crash is either in:- - # PathToolBit.Factory.CreateFromAttrs(attrs, name) - # or the rerun, as print immed before is printed, - # but NOT print back in main createRangeToolControllers code that called createTool - - # ALSO triggered by:- nonsensename, abc, endmil (ie missing one "l"), drll (missing "i") - # so instead try create with shape = None, then afetr set Tool.bitShape prop(check prop name) - - # ++ Test (creates path, sim AND bCNC) variety of tools in a qpJob, including test large tool in smll hole/slot.... - # ...this unrelated to centredrill issue! - # test above after creating new fc user prefs & dflt tools + create a cdrill shape -# chamfer -# ballend -# bullnose -# v-bit -# slittingsaw -# probe -# thread-mill - -DOC.recompute() -setview(DOC, VIEW) -print("--- done...but if does not look correct, close doc & run again...been having occasional 1st run issues ---") - - - - - - -# ------------------------------------------------------------------- -# method to obtain information from a tool file saved on disk, a bit rough -# toolspath eg "/home/user/.FreeCAD/Tools/Bit/" - -# T1 = PathToolBit.Declaration(toolspath + '/' + "1mm_Endmill.fctb") - -# TBits - # 1+ Libs - # 1+ ToolBits - # 1+ shapes - -# TestPathToolController.py - # create Tool, tehn create TC from tool - # create TC from template - # assert values & round trip values - -# TestPathToolBit.py - # check 3xdir/paths - # print dir tree - # PathToolBit heap of tests about finding tool{Bit/shape/lib} by name/shape/dir....... - -# TestPathTool.py - # create Path.Tool - # add atribs, assert OK & ditto for round trip - -# TestPathTooltable.py - # ToolTable = OLD style?? - - -# Job & TC - - -# ------------------- ex onekk -# tc0 = job.ToolController[0] -# tc0.Label = "MyTC1" -# tc0.setExpression('HorizRapid', None) -# tc0.HorizRapid = "15 mm/s" -# tc0.setExpression('VertRapid', None) -# tc0.VertRapid = "2 mm/s" - -# tct1 = tc0.Tool -# tct1.Label = T1['name'] -# tct1.BitShape = shapepath + T1['shape'] -# tct1.CuttingEdgeHeight = T1['parameter']['CuttingEdgeHeight'] -# tct1.Diameter = T1['parameter']['Diameter'] -# tct1.Length = T1['parameter']['Length'] -# tct1.ShankDiameter = T1['parameter']['ShankDiameter'] -# tct1.recompute() -# tct1.ViewObject.Visibility = True - -# tct1.recompute() -# tc0.recompute() -# ------------------------------------------------------------------- - -# tooldata from the tool file is composed like : -# so the tricks in T1[][] to extract the parameters to put into the proper field of the TC.Tool -# {'version': 2, 'name': '3mm Endmill', 'shape': 'endmill.fcstd', 'parameter': {'CuttingEdgeHeight': '17,00 mm', 'Diameter': '3,00 mm', 'Length': '50,00 mm', 'ShankDiameter': '3,17 mm'}, 'attribute': {}} - -# set this expression to 'None' and then assign values in the proper format Note the speed units. -# tc0.setExpression('HorizRapid', None) -# tc0.HorizRapid = "15 mm/s" -# tc0.setExpression('VertRapid', None) -# tc0.VertRapid = "2 mm/s" - -# other doubt is the file property of the ToolController.Tool part. -# But for now it seems quite useable to make some scripting. -# Main concerns for now are the tool shape and to find a way to apply the Tags dressup to a profile. -#------------------- ex onekk - - diff --git a/examples/scripted_use.FCMacro b/examples/scripted_use.FCMacro deleted file mode 100644 index 156a0ab..0000000 --- a/examples/scripted_use.FCMacro +++ /dev/null @@ -1,33 +0,0 @@ -# Demonstration script to run an extended version of the Feed and Speed Calculator FreeCAD addon, -# which can be installed from the "rpm_override" branch at https://github.com/spanner888/FeedsAndSpeeds/tree/rpm_override -# Original code is https://github.com/dubstar-04/FeedsAndSpeeds - -# This scripts demonstrates original Feed and Speed Calculator capability -# and also the added over ride limits set to match your CNC. - -# version 1.0 - -import PathFeedsAndSpeeds - -fsAddon = PathFeedsAndSpeeds.FSCalculation() - -# Easy set defaults -tool, limits = fsAddon.initDefaults() - -# Now Change defaults to suit your needs. See initFsDefaults() for details. -# Some examples:- -tool.toolDia = 4.0 -tool.flutes = 1 -fsAddon.material = "Aluminium (6061)" -limits.cncRpmMax = 10000 - -# Optional print of all the inputs -fsAddon.printInputs(tool, limits) - -# Original calculator behaviour, no SCRIPTED over rides -rpm, feed, Hp = fsAddon.calculate(tool) -# print("\t rpm %d, feed %d, Hp %.2f & Watts %d" % (rpm, feed, Hp, Hp*745.6999)) - -# auto override feed, rpm & power, based on cnc limit settings -rpm, feed, Hp = fsAddon.calcWithAutoOverrides(tool, limits) -print(" -- end Feeds and Speeds integration script/macro --")