- finished work in ToolSolderPaste

This commit is contained in:
Marius Stanciu 2019-02-21 23:48:13 +02:00 committed by Marius S
parent d5768d3b34
commit d453c31bf5
11 changed files with 442 additions and 273 deletions

View File

@ -489,8 +489,11 @@ class App(QtCore.QObject):
"tools_solderpaste_z_dispense": self.tools_defaults_form.tools_solderpaste_group.z_dispense_entry,
"tools_solderpaste_z_stop": self.tools_defaults_form.tools_solderpaste_group.z_stop_entry,
"tools_solderpaste_z_travel": self.tools_defaults_form.tools_solderpaste_group.z_travel_entry,
"tools_solderpaste_z_toolchange": self.tools_defaults_form.tools_solderpaste_group.z_toolchange_entry,
"tools_solderpaste_xy_toolchange": self.tools_defaults_form.tools_solderpaste_group.xy_toolchange_entry,
"tools_solderpaste_frxy": self.tools_defaults_form.tools_solderpaste_group.frxy_entry,
"tools_solderpaste_frz": self.tools_defaults_form.tools_solderpaste_group.frz_entry,
"tools_solderpaste_frz_dispense": self.tools_defaults_form.tools_solderpaste_group.frz_dispense_entry,
"tools_solderpaste_speedfwd": self.tools_defaults_form.tools_solderpaste_group.speedfwd_entry,
"tools_solderpaste_dwellfwd": self.tools_defaults_form.tools_solderpaste_group.dwellfwd_entry,
"tools_solderpaste_speedrev": self.tools_defaults_form.tools_solderpaste_group.speedrev_entry,
@ -499,16 +502,13 @@ class App(QtCore.QObject):
}
#############################
#### LOAD POSTPROCESSORS ####
#############################
self.postprocessors = load_postprocessors(self)
for name in list(self.postprocessors.keys()):
# 'Paste' postprocessors are to be used only in the Solder Paste Dispensing Tool
if name.partition('_')[0] == 'Paste':
self.tools_defaults_form.tools_solderpaste_group.pp_combo.addItem(name)
@ -743,13 +743,16 @@ class App(QtCore.QObject):
"tools_solderpaste_z_dispense": 0.01,
"tools_solderpaste_z_stop": 0.005,
"tools_solderpaste_z_travel": 0.1,
"tools_solderpaste_z_toolchange": 1.0,
"tools_solderpaste_xy_toolchange": "0.0, 0.0",
"tools_solderpaste_frxy": 3.0,
"tools_solderpaste_frz": 3.0,
"tools_solderpaste_frz_dispense": 1.0,
"tools_solderpaste_speedfwd": 20,
"tools_solderpaste_dwellfwd": 1,
"tools_solderpaste_speedrev": 10,
"tools_solderpaste_dwellrev": 1,
"tools_solderpaste_pp": ''
"tools_solderpaste_pp": 'Paste_1'
})
###############################
@ -1623,14 +1626,14 @@ class App(QtCore.QObject):
self.film_tool = Film(self)
self.film_tool.install(icon=QtGui.QIcon('share/film16.png'))
self.paste_tool = ToolSolderPaste(self)
self.paste_tool = SolderPaste(self)
self.paste_tool.install(icon=QtGui.QIcon('share/solderpastebis32.png'), separator=True)
self.move_tool = ToolMove(self)
self.move_tool.install(icon=QtGui.QIcon('share/move16.png'), pos=self.ui.menuedit,
before=self.ui.menueditorigin)
self.cutout_tool = ToolCutOut(self)
self.cutout_tool = CutOut(self)
self.cutout_tool.install(icon=QtGui.QIcon('share/cut16.png'), pos=self.ui.menutool,
before=self.measurement_tool.menuAction)

View File

@ -5224,14 +5224,33 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
grid0.addWidget(self.z_travel_label, 5, 0)
grid0.addWidget(self.z_travel_entry, 5, 1)
# Z toolchange location
self.z_toolchange_entry = FCEntry()
self.z_toolchange_label = QtWidgets.QLabel("Z Toolchange:")
self.z_toolchange_label.setToolTip(
"The height (Z) for tool (nozzle) change."
)
grid0.addWidget(self.z_toolchange_label, 6, 0)
grid0.addWidget(self.z_toolchange_entry, 6, 1)
# X,Y Toolchange location
self.xy_toolchange_entry = FCEntry()
self.xy_toolchange_label = QtWidgets.QLabel("XY Toolchange:")
self.xy_toolchange_label.setToolTip(
"The X,Y location for tool (nozzle) change.\n"
"The format is (x, y) where x and y are real numbers."
)
grid0.addWidget(self.xy_toolchange_label, 7, 0)
grid0.addWidget(self.xy_toolchange_entry, 7, 1)
# Feedrate X-Y
self.frxy_entry = FCEntry()
self.frxy_label = QtWidgets.QLabel("Feedrate X-Y:")
self.frxy_label.setToolTip(
"Feedrate (speed) while moving on the X-Y plane."
)
grid0.addWidget(self.frxy_label, 6, 0)
grid0.addWidget(self.frxy_entry, 6, 1)
grid0.addWidget(self.frxy_label, 8, 0)
grid0.addWidget(self.frxy_entry, 8, 1)
# Feedrate Z
self.frz_entry = FCEntry()
@ -5240,8 +5259,18 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
"Feedrate (speed) while moving vertically\n"
"(on Z plane)."
)
grid0.addWidget(self.frz_label, 7, 0)
grid0.addWidget(self.frz_entry, 7, 1)
grid0.addWidget(self.frz_label, 9, 0)
grid0.addWidget(self.frz_entry, 9, 1)
# Feedrate Z Dispense
self.frz_dispense_entry = FCEntry()
self.frz_dispense_label = QtWidgets.QLabel("Feedrate Z Dispense:")
self.frz_dispense_label.setToolTip(
"Feedrate (speed) while moving up vertically\n"
" to Dispense position (on Z plane)."
)
grid0.addWidget(self.frz_dispense_label, 10, 0)
grid0.addWidget(self.frz_dispense_entry, 10, 1)
# Spindle Speed Forward
self.speedfwd_entry = FCEntry()
@ -5250,8 +5279,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
"The dispenser speed while pushing solder paste\n"
"through the dispenser nozzle."
)
grid0.addWidget(self.speedfwd_label, 8, 0)
grid0.addWidget(self.speedfwd_entry, 8, 1)
grid0.addWidget(self.speedfwd_label, 11, 0)
grid0.addWidget(self.speedfwd_entry, 11, 1)
# Dwell Forward
self.dwellfwd_entry = FCEntry()
@ -5259,8 +5288,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
self.dwellfwd_label.setToolTip(
"Pause after solder dispensing."
)
grid0.addWidget(self.dwellfwd_label, 9, 0)
grid0.addWidget(self.dwellfwd_entry, 9, 1)
grid0.addWidget(self.dwellfwd_label, 12, 0)
grid0.addWidget(self.dwellfwd_entry, 12, 1)
# Spindle Speed Reverse
self.speedrev_entry = FCEntry()
@ -5269,8 +5298,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
"The dispenser speed while retracting solder paste\n"
"through the dispenser nozzle."
)
grid0.addWidget(self.speedrev_label, 10, 0)
grid0.addWidget(self.speedrev_entry, 10, 1)
grid0.addWidget(self.speedrev_label, 13, 0)
grid0.addWidget(self.speedrev_entry, 13, 1)
# Dwell Reverse
self.dwellrev_entry = FCEntry()
@ -5279,8 +5308,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
"Pause after solder paste dispenser retracted,\n"
"to allow pressure equilibrium."
)
grid0.addWidget(self.dwellrev_label, 11, 0)
grid0.addWidget(self.dwellrev_entry, 11, 1)
grid0.addWidget(self.dwellrev_label, 14, 0)
grid0.addWidget(self.dwellrev_entry, 14, 1)
# Postprocessors
pp_label = QtWidgets.QLabel('PostProcessors:')
@ -5289,8 +5318,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
)
self.pp_combo = FCComboBox()
grid0.addWidget(pp_label, 12, 0)
grid0.addWidget(self.pp_combo, 12, 1)
grid0.addWidget(pp_label, 15, 0)
grid0.addWidget(self.pp_combo, 15, 1)
self.layout.addStretch()

View File

@ -4695,6 +4695,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
'''
self.exc_cnc_tools = {}
# flag to store if the CNCJob is part of a special group of CNCJob objects that can't be processed by the
# default engine of FlatCAM. They generated by some of tools and are special cases of CNCJob objects.
self. special_group = None
# for now it show if the plot will be done for multi-tool CNCJob (True) or for single tool
# (like the one in the TCL Command), False
@ -4944,11 +4947,22 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
preamble = str(self.ui.prepend_text.get_value())
postamble = str(self.ui.append_text.get_value())
self.export_gcode(filename, preamble=preamble, postamble=postamble)
gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
if gc == 'fail':
return
self.app.file_saved.emit("gcode", filename)
self.app.inform.emit("[success] Machine Code file saved to: %s" % filename)
def on_modifygcode_button_click(self, *args):
preamble = str(self.ui.prepend_text.get_value())
postamble = str(self.ui.append_text.get_value())
gc = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
if gc == 'fail':
return
else:
self.app.gcode_edited = gc
# add the tab if it was closed
self.app.ui.plot_tab_area.addTab(self.app.ui.cncjob_tab, "Code Editor")
@ -4959,10 +4973,6 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
# Switch plot_area to CNCJob tab
self.app.ui.plot_tab_area.setCurrentWidget(self.app.ui.cncjob_tab)
preamble = str(self.ui.prepend_text.get_value())
postamble = str(self.ui.append_text.get_value())
self.app.gcode_edited = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
# first clear previous text in text editor (if any)
self.app.ui.code_editor.clear()
@ -5076,6 +5086,14 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
roland = False
hpgl = False
try:
if self.special_group:
self.app.inform.emit("[WARNING_NOTCL]This CNCJob object can't be processed because "
"it is a %s CNCJob object." % str(self.special_group))
return 'fail'
except AttributeError:
pass
# detect if using Roland postprocessor
try:
for key in self.cnc_tools:

View File

@ -20,6 +20,7 @@ class ABCPostProcRegister(ABCMeta):
postprocessors[newclass.__name__] = newclass() # here is your register function
return newclass
class FlatCAMPostProc(object, metaclass=ABCPostProcRegister):
@abstractmethod
def start_code(self, p):
@ -65,6 +66,77 @@ class FlatCAMPostProc(object, metaclass=ABCPostProcRegister):
def spindle_stop_code(self,p):
pass
class FlatCAMPostProc_Tools(object, metaclass=ABCPostProcRegister):
@abstractmethod
def start_code(self, p):
pass
@abstractmethod
def lift_code(self, p):
pass
@abstractmethod
def down_z_start_code(self, p):
pass
@abstractmethod
def lift_z_dispense_code(self, p):
pass
@abstractmethod
def down_z_stop_code(self, p):
pass
@abstractmethod
def toolchange_code(self, p):
pass
@abstractmethod
def rapid_code(self, p):
pass
@abstractmethod
def linear_code(self, p):
pass
@abstractmethod
def end_code(self, p):
pass
@abstractmethod
def feedrate_xy_code(self, p):
pass
@abstractmethod
def feedrate_z_code(self, p):
pass
@abstractmethod
def feedrate_z_dispense_code(self,p):
pass
@abstractmethod
def spindle_fwd_code(self,p):
pass
@abstractmethod
def spindle_rev_code(self,p):
pass
@abstractmethod
def spindle_off_code(self,p):
pass
@abstractmethod
def dwell_fwd_code(self,p):
pass
@abstractmethod
def dwell_rev_code(self,p):
pass
def load_postprocessors(app):
postprocessors_path_search = [os.path.join(app.data_path,'postprocessors','*.py'),
os.path.join('postprocessors', '*.py')]

View File

@ -19,6 +19,7 @@ CAD program, and create G-Code for Isolation routing.
- added protection against trying to create a CNCJob from a solder_paste dispenser geometry. This one is different than the default Geometry and can be handled only by SolderPaste Tool.
- ToolSolderPaste tools (nozzles) now have each it's own settings
- creating the camlib functions for the ToolSolderPaste gcode generation functions
- finished work in ToolSolderPaste
20.02.2019

241
camlib.py
View File

@ -4503,6 +4503,9 @@ class CNCjob(Geometry):
self.pp_excellon_name = pp_excellon_name
self.pp_excellon = self.app.postprocessors[self.pp_excellon_name]
self.pp_solderpaste_name = None
# Controls if the move from Z_Toolchange to Z_Move is done fast with G0 or normally with G1
self.f_plunge = None
@ -4527,6 +4530,8 @@ class CNCjob(Geometry):
self.oldx = None
self.oldy = None
self.tool = 0.0
# Attributes to be included in serialization
# Always append to it because it carries contents
# from Geometry.
@ -5191,99 +5196,6 @@ class CNCjob(Geometry):
return self.gcode
def generate_gcode_from_solderpaste_geo(self, **kwargs):
"""
Algorithm to generate from multitool Geometry.
Algorithm description:
----------------------
Uses RTree to find the nearest path to follow.
:return: Gcode string
"""
log.debug("Generate_from_solderpaste_geometry()")
## Index first and last points in paths
# What points to index.
def get_pts(o):
return [o.coords[0], o.coords[-1]]
self.gcode = ""
if not kwargs:
log.debug("camlib.generate_from_solderpaste_geo() --> No tool in the solderpaste geometry.")
self.app.inform.emit("[ERROR_NOTCL] There is no tool data in the SolderPaste geometry.")
# this is the tool diameter, it is used as such to accommodate the postprocessor who need the tool diameter
# given under the name 'toolC'
self.postdata['toolC'] = kwargs['tooldia']
# Initial G-Code
pp_solderpaste_name = kwargs['data']['tools_solderpaste_pp'] if kwargs['data']['tools_solderpaste_pp'] else \
self.app.defaults['tools_solderpaste_pp']
p = self.app.postprocessors[pp_solderpaste_name]
self.gcode = self.doformat(p.start_code)
## Flatten the geometry. Only linear elements (no polygons) remain.
flat_geometry = self.flatten(kwargs['solid_geometry'], pathonly=True)
log.debug("%d paths" % len(flat_geometry))
# Create the indexed storage.
storage = FlatCAMRTreeStorage()
storage.get_points = get_pts
# Store the geometry
log.debug("Indexing geometry before generating G-Code...")
for shape in flat_geometry:
if shape is not None:
storage.insert(shape)
# kwargs length will tell actually the number of tools used so if we have more than one tools then
# we have toolchange event
if len(kwargs) > 1:
self.gcode += self.doformat(p.toolchange_code)
else:
self.gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height
## Iterate over geometry paths getting the nearest each time.
log.debug("Starting SolderPaste G-Code...")
path_count = 0
current_pt = (0, 0)
pt, geo = storage.nearest(current_pt)
try:
while True:
path_count += 1
# Remove before modifying, otherwise deletion will fail.
storage.remove(geo)
# If last point in geometry is the nearest but prefer the first one if last point == first point
# then reverse coordinates.
if pt != geo.coords[0] and pt == geo.coords[-1]:
geo.coords = list(geo.coords)[::-1]
self.gcode += self.create_soldepaste_gcode(geo, p=p)
current_pt = geo.coords[-1]
pt, geo = storage.nearest(current_pt) # Next
except StopIteration: # Nothing found in storage.
pass
log.debug("Finishing SolderPste G-Code... %s paths traced." % path_count)
# Finish
self.gcode += self.doformat(p.lift_code)
self.gcode += self.doformat(p.end_code)
return self.gcode
def generate_from_geometry_2(self, geometry, append=True,
tooldia=None, offset=0.0, tolerance=0,
z_cut=1.0, z_move=2.0,
@ -5536,41 +5448,152 @@ class CNCjob(Geometry):
return self.gcode
def generate_gcode_from_solderpaste_geo(self, **kwargs):
"""
Algorithm to generate from multitool Geometry.
Algorithm description:
----------------------
Uses RTree to find the nearest path to follow.
:return: Gcode string
"""
log.debug("Generate_from_solderpaste_geometry()")
## Index first and last points in paths
# What points to index.
def get_pts(o):
return [o.coords[0], o.coords[-1]]
self.gcode = ""
if not kwargs:
log.debug("camlib.generate_from_solderpaste_geo() --> No tool in the solderpaste geometry.")
self.app.inform.emit("[ERROR_NOTCL] There is no tool data in the SolderPaste geometry.")
# this is the tool diameter, it is used as such to accommodate the postprocessor who need the tool diameter
# given under the name 'toolC'
self.postdata['z_start'] = kwargs['data']['tools_solderpaste_z_start']
self.postdata['z_dispense'] = kwargs['data']['tools_solderpaste_z_dispense']
self.postdata['z_stop'] = kwargs['data']['tools_solderpaste_z_stop']
self.postdata['z_travel'] = kwargs['data']['tools_solderpaste_z_travel']
self.postdata['z_toolchange'] = kwargs['data']['tools_solderpaste_z_toolchange']
self.postdata['xy_toolchange'] = kwargs['data']['tools_solderpaste_xy_toolchange']
self.postdata['frxy'] = kwargs['data']['tools_solderpaste_frxy']
self.postdata['frz'] = kwargs['data']['tools_solderpaste_frz']
self.postdata['frz_dispense'] = kwargs['data']['tools_solderpaste_frz_dispense']
self.postdata['speedfwd'] = kwargs['data']['tools_solderpaste_speedfwd']
self.postdata['dwellfwd'] = kwargs['data']['tools_solderpaste_dwellfwd']
self.postdata['speedrev'] = kwargs['data']['tools_solderpaste_speedrev']
self.postdata['dwellrev'] = kwargs['data']['tools_solderpaste_dwellrev']
self.postdata['pp_solderpaste_name'] = kwargs['data']['tools_solderpaste_pp']
self.postdata['toolC'] = kwargs['tooldia']
self.pp_solderpaste_name = kwargs['data']['tools_solderpaste_pp'] if kwargs['data']['tools_solderpaste_pp'] \
else self.app.defaults['tools_solderpaste_pp']
p = self.app.postprocessors[self.pp_solderpaste_name]
## Flatten the geometry. Only linear elements (no polygons) remain.
flat_geometry = self.flatten(kwargs['solid_geometry'], pathonly=True)
log.debug("%d paths" % len(flat_geometry))
# Create the indexed storage.
storage = FlatCAMRTreeStorage()
storage.get_points = get_pts
# Store the geometry
log.debug("Indexing geometry before generating G-Code...")
for shape in flat_geometry:
if shape is not None:
storage.insert(shape)
# Initial G-Code
self.gcode = self.doformat(p.start_code)
self.gcode += self.doformat(p.spindle_off_code)
self.gcode += self.doformat(p.toolchange_code)
## Iterate over geometry paths getting the nearest each time.
log.debug("Starting SolderPaste G-Code...")
path_count = 0
current_pt = (0, 0)
pt, geo = storage.nearest(current_pt)
try:
while True:
path_count += 1
# Remove before modifying, otherwise deletion will fail.
storage.remove(geo)
# If last point in geometry is the nearest but prefer the first one if last point == first point
# then reverse coordinates.
if pt != geo.coords[0] and pt == geo.coords[-1]:
geo.coords = list(geo.coords)[::-1]
self.gcode += self.create_soldepaste_gcode(geo, p=p)
current_pt = geo.coords[-1]
pt, geo = storage.nearest(current_pt) # Next
except StopIteration: # Nothing found in storage.
pass
log.debug("Finishing SolderPste G-Code... %s paths traced." % path_count)
# Finish
self.gcode += self.doformat(p.lift_code)
self.gcode += self.doformat(p.end_code)
return self.gcode
def create_soldepaste_gcode(self, geometry, p):
gcode = ''
path = self.segment(geometry.coords)
path = geometry.coords
if type(geometry) == LineString or type(geometry) == LinearRing:
# Move fast to 1st point
gcode += self.doformat(p.rapid_code) # Move to first point
gcode += self.doformat(p.rapid_code, x=path[0][0], y=path[0][1]) # Move to first point
# Move down to cutting depth
gcode += self.doformat(p.feedrate_z_code)
gcode += self.doformat(p.down_z_start_code)
gcode += self.doformat(p.spindle_on_fwd_code) # Start dispensing
gcode += self.doformat(p.spindle_fwd_code) # Start dispensing
gcode += self.doformat(p.dwell_fwd_code)
gcode += self.doformat(p.feedrate_z_dispense_code)
gcode += self.doformat(p.lift_z_dispense_code)
gcode += self.doformat(p.feedrate_xy_code)
# Cutting...
for pt in path[1:]:
gcode += self.doformat(p.linear_code) # Linear motion to point
gcode += self.doformat(p.linear_code, x=pt[0], y=pt[1]) # Linear motion to point
# Up to travelling height.
gcode += self.doformat(p.spindle_off_code) # Stop dispensing
gcode += self.doformat(p.spindle_on_rev_code)
gcode += self.doformat(p.spindle_rev_code)
gcode += self.doformat(p.down_z_stop_code)
gcode += self.doformat(p.spindle_off_code)
gcode += self.doformat(p.dwell_rev_code)
gcode += self.doformat(p.feedrate_z_code)
gcode += self.doformat(p.lift_code)
elif type(geometry) == Point:
gcode += self.doformat(p.linear_code) # Move to first point
gcode += self.doformat(p.linear_code, x=path[0][0], y=path[0][1]) # Move to first point
gcode += self.doformat(p.feedrate_z_code)
gcode += self.doformat(p.feedrate_z_dispense_code)
gcode += self.doformat(p.down_z_start_code)
gcode += self.doformat(p.spindle_on_fwd_code) # Start dispensing
# TODO A dwell time for dispensing?
gcode += self.doformat(p.spindle_fwd_code) # Start dispensing
gcode += self.doformat(p.dwell_fwd_code)
gcode += self.doformat(p.lift_z_dispense_code)
gcode += self.doformat(p.spindle_off_code) # Stop dispensing
gcode += self.doformat(p.spindle_on_rev_code)
gcode += self.doformat(p.down_z_stop_code)
gcode += self.doformat(p.spindle_rev_code)
gcode += self.doformat(p.spindle_off_code)
gcode += self.doformat(p.down_z_stop_code)
gcode += self.doformat(p.dwell_rev_code)
gcode += self.doformat(p.feedrate_z_code)
gcode += self.doformat(p.lift_code)
return gcode
@ -5685,7 +5708,8 @@ class CNCjob(Geometry):
else:
command['Z'] = 0
elif 'grbl_laser' in self.pp_excellon_name or 'grbl_laser' in self.pp_geometry_name:
elif 'grbl_laser' in self.pp_excellon_name or 'grbl_laser' in self.pp_geometry_name or \
(self.pp_solderpaste_name is not None and 'Paste' in self.pp_solderpaste_name):
match_lsr = re.search(r"X([\+-]?\d+.[\+-]?\d+)\s*Y([\+-]?\d+.[\+-]?\d+)", gline)
if match_lsr:
command['X'] = float(match_lsr.group(1).replace(" ", ""))
@ -5699,7 +5723,12 @@ class CNCjob(Geometry):
command['Z'] = 1
else:
command['Z'] = 0
elif self.pp_solderpaste is not None:
if 'Paste' in self.pp_solderpaste:
match_paste = re.search(r"X([\+-]?\d+.[\+-]?\d+)\s*Y([\+-]?\d+.[\+-]?\d+)", gline)
if match_paste:
command['X'] = float(match_paste.group(1).replace(" ", ""))
command['Y'] = float(match_paste.group(2).replace(" ", ""))
else:
match = re.search(r'^\s*([A-Z])\s*([\+\-\.\d\s]+)', gline)
while match:

View File

@ -1,14 +1,9 @@
from FlatCAMTool import FlatCAMTool
from copy import copy,deepcopy
from ObjectCollection import *
from FlatCAMApp import *
from PyQt5 import QtGui, QtCore, QtWidgets
from GUIElements import IntEntry, RadioSet, LengthEntry, FloatEntry
from FlatCAMObj import FlatCAMGeometry, FlatCAMExcellon, FlatCAMGerber
class ToolCutOut(FlatCAMTool):
class CutOut(FlatCAMTool):
toolName = "Cutout PCB"
@ -472,4 +467,3 @@ class ToolCutOut(FlatCAMTool):
def reset_fields(self):
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))

View File

@ -1,7 +1,5 @@
from FlatCAMTool import FlatCAMTool
from copy import copy,deepcopy
# from GUIElements import IntEntry, RadioSet, FCEntry
# from FlatCAMObj import FlatCAMGeometry, FlatCAMExcellon, FlatCAMGerber
from ObjectCollection import *
import time

View File

@ -9,7 +9,7 @@ from FlatCAMCommon import LoudDict
from FlatCAMObj import FlatCAMGeometry, FlatCAMExcellon, FlatCAMGerber
class ToolSolderPaste(FlatCAMTool):
class SolderPaste(FlatCAMTool):
toolName = "Solder Paste Tool"
@ -177,6 +177,23 @@ class ToolSolderPaste(FlatCAMTool):
)
self.gcode_form_layout.addRow(self.z_travel_label, self.z_travel_entry)
# Z toolchange location
self.z_toolchange_entry = FCEntry()
self.z_toolchange_label = QtWidgets.QLabel("Z Toolchange:")
self.z_toolchange_label.setToolTip(
"The height (Z) for tool (nozzle) change."
)
self.gcode_form_layout.addRow(self.z_toolchange_label, self.z_toolchange_entry)
# X,Y Toolchange location
self.xy_toolchange_entry = FCEntry()
self.xy_toolchange_label = QtWidgets.QLabel("XY Toolchange:")
self.xy_toolchange_label.setToolTip(
"The X,Y location for tool (nozzle) change.\n"
"The format is (x, y) where x and y are real numbers."
)
self.gcode_form_layout.addRow(self.xy_toolchange_label, self.xy_toolchange_entry)
# Feedrate X-Y
self.frxy_entry = FCEntry()
self.frxy_label = QtWidgets.QLabel("Feedrate X-Y:")
@ -194,6 +211,15 @@ class ToolSolderPaste(FlatCAMTool):
)
self.gcode_form_layout.addRow(self.frz_label, self.frz_entry)
# Feedrate Z Dispense
self.frz_dispense_entry = FCEntry()
self.frz_dispense_label = QtWidgets.QLabel("Feedrate Z Dispense:")
self.frz_dispense_label.setToolTip(
"Feedrate (speed) while moving up vertically\n"
" to Dispense position (on Z plane)."
)
self.gcode_form_layout.addRow(self.frz_dispense_label, self.frz_dispense_entry)
# Spindle Speed Forward
self.speedfwd_entry = FCEntry()
self.speedfwd_label = QtWidgets.QLabel("Spindle Speed FWD:")
@ -332,6 +358,8 @@ class ToolSolderPaste(FlatCAMTool):
self.options = LoudDict()
self.form_fields = {}
self.units = ''
## Signals
self.addtool_btn.clicked.connect(self.on_tool_add)
self.deltool_btn.clicked.connect(self.on_tool_delete)
@ -366,8 +394,11 @@ class ToolSolderPaste(FlatCAMTool):
"tools_solderpaste_z_dispense": self.z_dispense_entry,
"tools_solderpaste_z_stop": self.z_stop_entry,
"tools_solderpaste_z_travel": self.z_travel_entry,
"tools_solderpaste_z_toolchange": self.z_toolchange_entry,
"tools_solderpaste_xy_toolchange": self.xy_toolchange_entry,
"tools_solderpaste_frxy": self.frxy_entry,
"tools_solderpaste_frz": self.frz_entry,
"tools_solderpaste_frz_dispense": self.frz_dispense_entry,
"tools_solderpaste_speedfwd": self.speedfwd_entry,
"tools_solderpaste_dwellfwd": self.dwellfwd_entry,
"tools_solderpaste_speedrev": self.speedrev_entry,
@ -707,7 +738,6 @@ class ToolSolderPaste(FlatCAMTool):
self.ui_disconnect()
deleted_tools_list = []
if all:
self.tools.clear()
self.build_ui()
@ -810,12 +840,17 @@ class ToolSolderPaste(FlatCAMTool):
diagonal_1 = LineString([min, max])
diagonal_2 = LineString([min_r, max_r])
round_diag_1 = round(diagonal_1.intersection(p).length, 2)
round_diag_2 = round(diagonal_2.intersection(p).length, 2)
if self.units == 'MM':
round_diag_1 = round(diagonal_1.intersection(p).length, 1)
round_diag_2 = round(diagonal_2.intersection(p).length, 1)
else:
round_diag_1 = round(diagonal_1.intersection(p).length, 2)
round_diag_2 = round(diagonal_2.intersection(p).length, 2)
if round_diag_1 == round_diag_2:
l = distance((xmin, ymin), (xmax, ymin))
h = distance((xmin, ymin), (xmin, ymax))
if offset >= l /2 or offset >= h / 2:
return "fail"
if l > h:
@ -859,7 +894,7 @@ class ToolSolderPaste(FlatCAMTool):
geo_obj.tools[tooluid]['offset'] = 'Path'
geo_obj.tools[tooluid]['offset_value'] = 0.0
geo_obj.tools[tooluid]['type'] = 'SolderPaste'
geo_obj.tools[tooluid]['tool_type'] = 'Dispenser Nozzle'
geo_obj.tools[tooluid]['tool_type'] = 'DN'
for g in work_geo:
if type(g) == MultiPolygon:
@ -914,6 +949,8 @@ class ToolSolderPaste(FlatCAMTool):
# self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
def on_view_gcode(self):
time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
# add the tab if it was closed
self.app.ui.plot_tab_area.addTab(self.app.ui.cncjob_tab, "Code Editor")
@ -923,15 +960,40 @@ class ToolSolderPaste(FlatCAMTool):
name = self.cnc_obj_combo.currentText()
obj = self.app.collection.get_by_name(name)
try:
if obj.special_group != 'solder_paste_tool':
self.app.inform.emit("[WARNING_NOTCL]This CNCJob object can't be processed. "
"NOT a solder_paste_tool CNCJob object.")
return
except AttributeError:
self.app.inform.emit("[WARNING_NOTCL]This CNCJob object can't be processed. "
"NOT a solder_paste_tool CNCJob object.")
return
gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \
(str(self.app.version), str(self.app.version_date)) + '\n'
gcode += '(Name: ' + str(name) + ')\n'
gcode += '(Type: ' + "G-code from " + str(obj.options['type']) + " for Solder Paste dispenser" + ')\n'
# if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
# gcode += '(Tools in use: ' + str(p['options']['Tools_in_use']) + ')\n'
gcode += '(Units: ' + self.units.upper() + ')\n' + "\n"
gcode += '(Created on ' + time_str + ')\n' + '\n'
for tool in obj.cnc_tools:
gcode += obj.cnc_tools[tool]['gcode']
# then append the text from GCode to the text editor
try:
file = StringIO(obj.gcode)
lines = StringIO(gcode)
except:
self.app.inform.emit("[ERROR_NOTCL] No Gcode in the object...")
return
try:
for line in file:
for line in lines:
proc_line = str(line).strip('\n')
self.app.ui.code_editor.append(proc_line)
except Exception as e:
@ -949,6 +1011,11 @@ class ToolSolderPaste(FlatCAMTool):
name = self.cnc_obj_combo.currentText()
obj = self.app.collection.get_by_name(name)
if obj.special_group != 'solder_paste_tool':
self.app.inform.emit("[WARNING_NOTCL]This CNCJob object can't be processed. "
"NOT a solder_paste_tool CNCJob object.")
return
_filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.cnc);;" \
"G-Code Files (*.g-code);;All Files (*.*)"
@ -978,7 +1045,8 @@ class ToolSolderPaste(FlatCAMTool):
gcode += '(Units: ' + self.units.upper() + ')\n' + "\n"
gcode += '(Created on ' + time_str + ')\n' + '\n'
gcode += obj.gcode
for tool in obj.cnc_tools:
gcode += obj.cnc_tools[tool]['gcode']
lines = StringIO(gcode)
## Write
@ -1040,6 +1108,7 @@ class ToolSolderPaste(FlatCAMTool):
job_obj.multitool = True
job_obj.multigeo = True
job_obj.cnc_tools.clear()
job_obj.special_group = 'solder_paste_tool'
job_obj.options['xmin'] = xmin
job_obj.options['ymin'] = ymin
@ -1063,13 +1132,14 @@ class ToolSolderPaste(FlatCAMTool):
job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"]
job_obj.fr_decimals = self.app.defaults["cncjob_fr_decimals"]
job_obj.tool = int(tooluid_key)
# Propagate options
job_obj.options["tooldia"] = tool_dia
job_obj.options['tool_dia'] = tool_dia
### CREATE GCODE ###
res = job_obj.generate_gcode_from_solderpaste_geo(**tool_cnc_dict)
res = job_obj.generate_gcode_from_solderpaste_geo(**tooluid_value)
if res == 'fail':
log.debug("FlatCAMGeometry.mtool_gen_cncjob() --> generate_from_geometry2() failed")
@ -1089,7 +1159,7 @@ class ToolSolderPaste(FlatCAMTool):
app_obj.progress.emit(80)
job_obj.cnc_tools.update({
tooluid_key: copy.deepcopy(tool_cnc_dict)
tooluid_key: deepcopy(tool_cnc_dict)
})
tool_cnc_dict.clear()

View File

@ -6,13 +6,13 @@ from flatcamTools.ToolFilm import Film
from flatcamTools.ToolMove import ToolMove
from flatcamTools.ToolDblSided import DblSidedTool
from flatcamTools.ToolCutOut import ToolCutOut
from flatcamTools.ToolCutOut import CutOut
from flatcamTools.ToolCalculators import ToolCalculator
from flatcamTools.ToolProperties import Properties
from flatcamTools.ToolImage import ToolImage
from flatcamTools.ToolPaint import ToolPaint
from flatcamTools.ToolNonCopperClear import NonCopperClear
from flatcamTools.ToolTransform import ToolTransform
from flatcamTools.ToolSolderPaste import ToolSolderPaste
from flatcamTools.ToolSolderPaste import SolderPaste
from flatcamTools.ToolShell import FCShell

View File

@ -1,14 +1,15 @@
from FlatCAMPostProc import *
class Paste_1(FlatCAMPostProc):
class Paste_1(FlatCAMPostProc_Tools):
coordinate_format = "%.*f"
feedrate_format = '%.*f'
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['toolchange_xy']
coords_xy = [float(eval(a)) for a in p['xy_toolchange'].split(",")]
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
@ -16,181 +17,135 @@ class Paste_1(FlatCAMPostProc):
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate_XY: ' + str(p['frxy']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['frz']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z_Dispense: ' + str(p['frz_dispense']) + units + '/min' + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Z_Dispense_Start: ' + str(p['z_start']) + units + ')\n'
gcode += '(Z_Dispense: ' + str(p['z_dispense']) + units + ')\n'
gcode += '(Z_Dispense_Stop: ' + str(p['z_stop']) + units + ')\n'
gcode += '(Z_Travel: ' + str(p['z_travel']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_Z: ' + str(p['feedrate_z']) + units + '/min' + ')\n'
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['endz']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
if 'Paste' in p.pp_solderpaste_name:
gcode += '(Postprocessor SolderPaste Dispensing Geometry: ' + str(p.pp_solderpaste_name) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
gcode += '(Spindle Speed FWD: %s RPM)\n' % str(p['speedfwd'])
gcode += '(Spindle Speed REV: %s RPM)\n' % str(p['speedrev'])
gcode += '(Dwell FWD: %s RPM)\n' % str(p['dwellfwd'])
gcode += '(Dwell REV: %s RPM)\n' % str(p['dwellrev'])
gcode += ('G20\n' if p.units.upper() == 'IN' else 'G21\n')
gcode += 'G90\n'
gcode += 'G94\n'
return gcode
def startz_code(self, p):
if p.startz is not None:
return 'G00 Z' + self.coordinate_format%(p.coords_decimals, p.startz)
else:
return ''
def lift_code(self, p):
return 'G00 Z' + self.coordinate_format%(p.coords_decimals, p.z_move)
return 'G00 Z' + self.coordinate_format%(p.coords_decimals, float(p['z_travel']))
def down_code(self, p):
return 'G01 Z' + self.coordinate_format%(p.coords_decimals, p.z_cut)
def down_z_start_code(self, p):
return 'G01 Z' + self.coordinate_format%(p.coords_decimals, float(p['z_start']))
def lift_z_dispense_code(self, p):
return 'G01 Z' + self.coordinate_format%(p.coords_decimals, float(p['z_dispense']))
def down_z_stop_code(self, p):
return 'G01 Z' + self.coordinate_format%(p.coords_decimals, float(p['z_stop']))
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
f_plunge = p.f_plunge
toolchangez = float(p['z_toolchange'])
toolchangexy = [float(eval(a)) for a in p['xy_toolchange'].split(",")]
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
no_drills = 1
if int(p.tool) == 1 and p.startz is not None:
toolchangez = p.startz
if p.units.upper() == 'MM':
toolC_formatted = format(p.toolC, '.2f')
toolC_formatted = format(float(p['toolC']), '.2f')
else:
toolC_formatted = format(p.toolC, '.4f')
toolC_formatted = format(float(p['toolC']), '.4f')
if str(p['options']['type']) == 'Excellon':
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
if toolchangexy is not None:
gcode = """
M5
G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
T{tool}
M6
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """
M5
G00 Z{toolchangez}
T{tool}
M6
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
else:
if toolchangexy is not None:
gcode = """
M5
if toolchangexy is not None:
gcode = """
G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
T{tool}
M6
(MSG, Change to Tool Dia = {toolC})
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
else:
gcode = """
M5
(MSG, Change to Tool with Nozzle Dia = {toolC})
M0
""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(int(p.tool)),
toolC=toolC_formatted)
else:
gcode = """
G00 Z{toolchangez}
T{tool}
M6
(MSG, Change to Tool Dia = {toolC})
M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
(MSG, Change to Tool with Nozzle Dia = {toolC})
M0
""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(int(p.tool)),
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
def up_to_zero_code(self, p):
return 'G01 Z0'
return gcode
def position_code(self, p):
return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \
(p.coords_decimals, p.x, p.coords_decimals, p.y)
def rapid_code(self, p):
return ('G00 ' + self.position_code(p)).format(**p)
return ('G00 ' + self.position_code(p)).format(**p) + '\nG00 Z' + \
self.coordinate_format%(p.coords_decimals, float(p['z_travel']))
def linear_code(self, p):
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
coords_xy = [float(eval(a)) for a in p['xy_toolchange'].split(",")]
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, float(p['z_toolchange'])) + "\n")
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate))
def feedrate_xy_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, float(p['frxy'])))
def feedrate_z_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate_z))
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, float(p['frz'])))
def spindle_code(self, p):
def feedrate_z_dispense_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, float(p['frz_dispense'])))
def spindle_fwd_code(self, p):
if p.spindlespeed:
return 'M03 S' + str(p.spindlespeed)
return 'M03 S' + str(float(p['speedfwd']))
else:
return 'M03'
def dwell_code(self, p):
if p.dwelltime:
return 'G4 P' + str(p.dwelltime)
def spindle_rev_code(self, p):
if p.spindlespeed:
return 'M04 S' + str(float(p['speedrev']))
else:
return 'M04'
def spindle_stop_code(self,p):
def spindle_off_code(self,p):
return 'M05'
def dwell_fwd_code(self, p):
if p.dwelltime:
return 'G4 P' + str(float(p['dwellfwd']))
def dwell_rev_code(self, p):
if p.dwelltime:
return 'G4 P' + str(float(p['dwellrev']))