From 0caa2c97f6c95c506373fa185b66782396d113d1 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 25 Apr 2022 00:01:11 +0300 Subject: [PATCH] - in SolderPaste Plugin fixed an issue where the new parameter 'margin' was taken from the current UI and applied to all tools (nozzles); now each nozzle use his 'margin' parameter - in SolderPaste Plugin make sure that the preprocessor is set the same for all tools (nozzles) whenever is modified - added 2 new preprocessor files for the SolderPaste Plugin (GRBL and MARLIN) - in SolderPaste Plugin made sure that the start GCode is not added for each nozzle but only once at start - in SolderPaste Plugin added a new parameter: 'Feedrate Rapids' which will set the feedrate for the fast moves as opposed with the previously used G0 command. This allows to fine tune the fast moves to reduce the vibrations --- CHANGELOG.md | 5 + appGUI/preferences/PreferencesUIManager.py | 1 + .../tools/ToolsSolderpastePrefGroupUI.py | 46 ++-- appPlugins/ToolImage.py | 4 +- appPlugins/ToolSolderPaste.py | 136 +++++++--- camlib.py | 10 +- defaults.py | 1 + preprocessors/Paste_1.py | 150 ++++++++--- preprocessors/Paste_GRBL.py | 253 ++++++++++++++++++ preprocessors/Paste_Marlin.py | 252 +++++++++++++++++ 10 files changed, 758 insertions(+), 100 deletions(-) create mode 100644 preprocessors/Paste_GRBL.py create mode 100644 preprocessors/Paste_Marlin.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d1dc8b1..1d1c3acd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ CHANGELOG for FlatCAM Evo beta - some fixes in the Import Image Plugin - made sure that the CNCJob objects made out of non-multi-toolGeometries are plotted correctly +- in SolderPaste Plugin fixed an issue where the new parameter 'margin' was taken from the current UI and applied to all tools (nozzles); now each nozzle use his 'margin' parameter +- in SolderPaste Plugin make sure that the preprocessor is set the same for all tools (nozzles) whenever is modified +- added 2 new preprocessor files for the SolderPaste Plugin (GRBL and MARLIN) +- in SolderPaste Plugin made sure that the start GCode is not added for each nozzle but only once at start +- in SolderPaste Plugin added a new parameter: 'Feedrate Rapids' which will set the feedrate for the fast moves as opposed with the previously used G0 command. This allows to fine tune the fast moves to reduce the vibrations 23.04.2022 diff --git a/appGUI/preferences/PreferencesUIManager.py b/appGUI/preferences/PreferencesUIManager.py index 0e295799..b9814d56 100644 --- a/appGUI/preferences/PreferencesUIManager.py +++ b/appGUI/preferences/PreferencesUIManager.py @@ -594,6 +594,7 @@ class PreferencesUIManager(QtCore.QObject): "tools_solderpaste_xy_toolchange": self.ui.plugin_pref_form.tools_solderpaste_group.xy_toolchange_entry, "tools_solderpaste_frxy": self.ui.plugin_pref_form.tools_solderpaste_group.frxy_entry, "tools_solderpaste_frz": self.ui.plugin_pref_form.tools_solderpaste_group.frz_entry, + "tools_solderpaste_fr_rapids": self.ui.plugin_pref_form.tools_solderpaste_group.fr_rapids_entry, "tools_solderpaste_frz_dispense": self.ui.plugin_pref_form.tools_solderpaste_group.frz_dispense_entry, "tools_solderpaste_speedfwd": self.ui.plugin_pref_form.tools_solderpaste_group.speedfwd_entry, "tools_solderpaste_dwellfwd": self.ui.plugin_pref_form.tools_solderpaste_group.dwellfwd_entry, diff --git a/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py b/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py index 218c71d9..f17a1618 100644 --- a/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py @@ -115,7 +115,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("The height (Z) when solder paste dispensing stops.") ) param_grid.addWidget(self.z_stop_label, 10, 0) - param_grid.addWidget(self.z_stop_entry, 101, 1) + param_grid.addWidget(self.z_stop_entry, 10, 1) # Z travel self.z_travel_entry = FCDoubleSpinner() @@ -167,6 +167,20 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): param_grid.addWidget(self.frxy_label, 18, 0) param_grid.addWidget(self.frxy_entry, 18, 1) + # Feedrate Rapids + self.frapids_lbl = FCLabel('%s:' % _("Feedrate Rapids")) + self.frapids_lbl.setToolTip( + _("Feedrate while moving as fast as possible.") + ) + + self.fr_rapids_entry = FCDoubleSpinner() + self.fr_rapids_entry.set_range(0.0000, 10000.0000) + self.fr_rapids_entry.set_precision(self.decimals) + self.fr_rapids_entry.setSingleStep(0.1) + + param_grid.addWidget(self.frapids_lbl, 20, 0) + param_grid.addWidget(self.fr_rapids_entry, 20, 1) + # Feedrate Z self.frz_entry = FCDoubleSpinner() self.frz_entry.set_precision(self.decimals) @@ -178,8 +192,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("Feedrate (speed) while moving vertically\n" "(on Z plane).") ) - param_grid.addWidget(self.frz_label, 20, 0) - param_grid.addWidget(self.frz_entry, 20, 1) + param_grid.addWidget(self.frz_label, 22, 0) + param_grid.addWidget(self.frz_entry, 22, 1) # Feedrate Z Dispense self.frz_dispense_entry = FCDoubleSpinner() @@ -192,8 +206,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("Feedrate (speed) while moving up vertically\n" "to Dispense position (on Z plane).") ) - param_grid.addWidget(self.frz_dispense_label, 22, 0) - param_grid.addWidget(self.frz_dispense_entry, 22, 1) + param_grid.addWidget(self.frz_dispense_label, 24, 0) + param_grid.addWidget(self.frz_dispense_entry, 24, 1) # Spindle Speed Forward self.speedfwd_entry = FCSpinner() @@ -205,8 +219,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("The dispenser speed while pushing solder paste\n" "through the dispenser nozzle.") ) - param_grid.addWidget(self.speedfwd_label, 24, 0) - param_grid.addWidget(self.speedfwd_entry, 24, 1) + param_grid.addWidget(self.speedfwd_label, 26, 0) + param_grid.addWidget(self.speedfwd_entry, 26, 1) # Dwell Forward self.dwellfwd_entry = FCDoubleSpinner() @@ -218,8 +232,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.dwellfwd_label.setToolTip( _("Pause after solder dispensing.") ) - param_grid.addWidget(self.dwellfwd_label, 26, 0) - param_grid.addWidget(self.dwellfwd_entry, 26, 1) + param_grid.addWidget(self.dwellfwd_label, 28, 0) + param_grid.addWidget(self.dwellfwd_entry, 28, 1) # Spindle Speed Reverse self.speedrev_entry = FCSpinner() @@ -231,8 +245,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("The dispenser speed while retracting solder paste\n" "through the dispenser nozzle.") ) - param_grid.addWidget(self.speedrev_label, 28, 0) - param_grid.addWidget(self.speedrev_entry, 28, 1) + param_grid.addWidget(self.speedrev_label, 30, 0) + param_grid.addWidget(self.speedrev_entry, 30, 1) # Dwell Reverse self.dwellrev_entry = FCDoubleSpinner() @@ -245,8 +259,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("Pause after solder paste dispenser retracted,\n" "to allow pressure equilibrium.") ) - param_grid.addWidget(self.dwellrev_label, 30, 0) - param_grid.addWidget(self.dwellrev_entry, 30, 1) + param_grid.addWidget(self.dwellrev_label, 32, 0) + param_grid.addWidget(self.dwellrev_entry, 32, 1) # Preprocessors pp_label = FCLabel('%s:' % _('Preprocessor')) @@ -261,7 +275,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): for it in range(self.pp_combo.count()): self.pp_combo.setItemData(it, self.pp_combo.itemText(it), QtCore.Qt.ItemDataRole.ToolTipRole) - param_grid.addWidget(pp_label, 32, 0) - param_grid.addWidget(self.pp_combo, 32, 1) + param_grid.addWidget(pp_label, 34, 0) + param_grid.addWidget(self.pp_combo, 34, 1) - self.layout.addStretch() + self.layout.addStretch(1) diff --git a/appPlugins/ToolImage.py b/appPlugins/ToolImage.py index 7c9f3bc8..380b60c7 100644 --- a/appPlugins/ToolImage.py +++ b/appPlugins/ToolImage.py @@ -162,7 +162,7 @@ class ToolImage(AppTool): txt = _("The tracing require Chromium,\n" "but it was not detected.\n" "\n" - "Do you want to download (about 300MB)?") + "Do you want to download it (about 300MB)?") msgbox.setWindowTitle(title) # taskbar still shows it msgbox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/app128.png')) msgbox.setText('%s' % title) @@ -179,7 +179,7 @@ class ToolImage(AppTool): if response == bt_no: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled.")) return - self.app.inform.emit(_("Please be patient. Chromium is being downloaded in background.\n" + self.app.inform.emit(_("Please be patient. Chromium is being downloaded in the background.\n" "The app will resume after it is installed.")) _filter = "Image Files(*.BMP *.PNG *.JPG *.JPEG);;" \ diff --git a/appPlugins/ToolSolderPaste.py b/appPlugins/ToolSolderPaste.py index 1a7efd3e..533d18f8 100644 --- a/appPlugins/ToolSolderPaste.py +++ b/appPlugins/ToolSolderPaste.py @@ -39,6 +39,7 @@ class SolderPaste(AppTool): self.obj_options = LoudDict() self.form_fields = {} + self.general_form_fields = {} self.units = '' self.name = "" @@ -128,6 +129,8 @@ class SolderPaste(AppTool): def connect_signals_at_init(self): self.ui.combo_context_del_action.triggered.connect(self.on_delete_object) + self.ui.tools_table.horizontalHeader().sectionClicked.connect(self.on_toggle_all_rows) + self.ui.addtool_btn.clicked.connect(self.on_tool_add) self.ui.addtool_entry.returnPressed.connect(self.on_tool_add) self.ui.deltool_btn.clicked.connect(self.on_tool_delete) @@ -142,6 +145,26 @@ class SolderPaste(AppTool): self.app.object_status_changed.connect(self.update_comboboxes) self.ui.reset_button.clicked.connect(self.set_tool_ui) + def on_toggle_all_rows(self): + """ + + :return: + :rtype: + """ + + sel_model = self.ui.tools_table.selectionModel() + sel_indexes = sel_model.selectedIndexes() + + # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + sel_rows = set() + for idx in sel_indexes: + sel_rows.add(idx.row()) + + if len(sel_rows) == self.ui.tools_table.rowCount(): + self.ui.tools_table.clearSelection() + else: + self.ui.tools_table.selectAll() + def set_tool_ui(self): self.clear_ui(self.layout) self.ui = SolderUI(layout=self.layout, app=self.app, solder_class=self) @@ -158,6 +181,7 @@ class SolderPaste(AppTool): "tools_solderpaste_z_toolchange": self.ui.z_toolchange_entry, "tools_solderpaste_xy_toolchange": self.ui.xy_toolchange_entry, "tools_solderpaste_frxy": self.ui.frxy_entry, + "tools_solderpaste_fr_rapids": self.ui.fr_rapids_entry, "tools_solderpaste_frz": self.ui.frz_entry, "tools_solderpaste_frz_dispense": self.ui.frz_dispense_entry, "tools_solderpaste_speedfwd": self.ui.speedfwd_entry, @@ -168,6 +192,10 @@ class SolderPaste(AppTool): }) self.set_form_from_defaults() + self.general_form_fields.update({ + "tools_solderpaste_pp": self.ui.pp_combo + }) + for option in self.app.options: if option.find('tools_') == 0: self.obj_options[option] = deepcopy(self.app.options[option]) @@ -377,7 +405,7 @@ class SolderPaste(AppTool): wdg.returnPressed.connect(self.form_to_storage) self.ui.tools_table.itemChanged.connect(self.on_tool_edit) - self.ui.tools_table.currentItemChanged.connect(self.on_row_selection_change) + self.ui.tools_table.itemSelectionChanged.connect(self.on_row_selection_change) def ui_disconnect(self): # if connected, disconnect the signal from the slot on item_changed as it creates issues @@ -407,7 +435,7 @@ class SolderPaste(AppTool): pass try: - self.ui.tools_table.currentItemChanged.disconnect(self.on_row_selection_change) + self.ui.tools_table.itemSelectionChanged.disconnect(self.on_row_selection_change) except (TypeError, AttributeError): pass @@ -465,6 +493,13 @@ class SolderPaste(AppTool): key: self.form_fields[key].get_value() }) + # set General Parameters for all tools; always done last + for key in self.general_form_fields: + for uid in self.tooltable_tools: + self.tooltable_tools[uid]['data'].update({ + key: self.general_form_fields[key].get_value() + }) + def set_form_from_defaults(self): """ Will read all the parameters of Solder Paste Tool from the app self.defaults and update the UI @@ -728,9 +763,6 @@ class SolderPaste(AppTool): :return: a Geometry type object """ - # this is a percentage of the tool diameter - tool_margin = self.ui.margin_entry.get_value() - proc = self.app.proc_container.new('%s...' % _("Working")) obj = work_object @@ -808,7 +840,6 @@ class SolderPaste(AppTool): tooluid = 1 for tool in sorted_tools: - offset = ((tool_margin * tool) * 0.01) + (tool / 2) for uid, vl in self.tooltable_tools.items(): if float('%.*f' % (self.decimals, float(vl['tooldia']))) == tool: tooluid = int(uid) @@ -825,6 +856,10 @@ class SolderPaste(AppTool): geo_obj.tools[tooluid]['data']['tools_mill_job_type'] = 'SP' # '' geo_obj.tools[tooluid]['data']['tools_mill_tool_shape'] = 'DN' # 'DN' + # this is a percentage of the tool diameter + tool_margin = geo_obj.tools[tooluid]['data']['tools_solderpaste_margin'] + offset = ((tool_margin * tool) * 0.01) + (tool / 2) + # self.flat_geometry is a list of LinearRings produced by flatten() from the exteriors of the Polygons # We get possible issues if we try to directly use the Polygons, due of possible the interiors, # so we do a hack: get first the exterior in a form of LinearRings and then convert back to Polygon @@ -985,8 +1020,9 @@ class SolderPaste(AppTool): # this turn on the FlatCAMCNCJob plot for multiple tools new_obj.multitool = True new_obj.multigeo = True - # new_obj object is a CNCJob object made from an Geometry object + # new_obj object is a CNCJob object made from a Geometry object new_obj.tools.clear() + new_obj.tools = obj.tools new_obj.special_group = 'solder_paste_tool' new_obj.obj_options['xmin'] = xmin @@ -995,7 +1031,7 @@ class SolderPaste(AppTool): new_obj.obj_options['ymax'] = ymax total_gcode = '' - for tooluid_key, tooluid_value in obj.tools.items(): + for tooluid_key, tooluid_value in new_obj.tools.items(): # find the tool_dia associated with the tooluid_key tool_dia = tooluid_value['tooldia'] tool_cnc_dict = deepcopy(tooluid_value) @@ -1009,7 +1045,8 @@ class SolderPaste(AppTool): new_obj.obj_options['tool_dia'] = tool_dia # ## CREATE GCODE # ## - res = new_obj.generate_gcode_from_solderpaste_geo(**tooluid_value) + is_first = True if tooluid_key == list(new_obj.tools.keys())[0] else False + res = new_obj.generate_gcode_from_solderpaste_geo(is_first=is_first, **tooluid_value) if res == 'fail': app_obj.log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed") @@ -1265,6 +1302,7 @@ class SolderUI: tt_frame.setLayout(tool_grid) self.tools_table = FCTable() + self.tools_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) tool_grid.addWidget(self.tools_table, 0, 0, 1, 4) self.tools_table.setColumnCount(3) @@ -1318,7 +1356,7 @@ class SolderUI: # grid0.addWidget(separator_line, 2, 0, 1, 4) # ############################################################################################################# - # Parameters Frame + # General Parameters Frame # ############################################################################################################# self.param_label = FCLabel('%s' % _("Parameters"), color='blue', bold=True) self.param_label.setToolTip( @@ -1332,21 +1370,7 @@ class SolderUI: param_grid = GLay(v_spacing=5, h_spacing=3) par_frame.setLayout(param_grid) - # Z travel - self.z_travel_entry = FCDoubleSpinner(callback=self.confirmation_message) - self.z_travel_entry.set_range(0.0000001, 10000.0000) - self.z_travel_entry.set_precision(self.decimals) - self.z_travel_entry.setSingleStep(0.1) - - self.z_travel_label = FCLabel('%s:' % _("Travel Z")) - self.z_travel_label.setToolTip( - _("The height (Z) for travel between pads\n" - "(without dispensing solder paste).") - ) - param_grid.addWidget(self.z_travel_label, 0, 0) - param_grid.addWidget(self.z_travel_entry, 0, 1) - - # MARGIN + # Margin self.margin_label = FCLabel('%s:' % _("Margin")) self.margin_label.setToolTip('%s %s' % ( _("Offset from the boundary."), @@ -1358,8 +1382,22 @@ class SolderUI: self.margin_entry.set_precision(self.decimals) self.margin_entry.setSingleStep(0.1) - param_grid.addWidget(self.margin_label, 2, 0) - param_grid.addWidget(self.margin_entry, 2, 1) + param_grid.addWidget(self.margin_label, 0, 0) + param_grid.addWidget(self.margin_entry, 0, 1) + + # Z travel + self.z_travel_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.z_travel_entry.set_range(0.0000001, 10000.0000) + self.z_travel_entry.set_precision(self.decimals) + self.z_travel_entry.setSingleStep(0.1) + + self.z_travel_label = FCLabel('%s:' % _("Travel Z")) + self.z_travel_label.setToolTip( + _("The height (Z) for travel between pads\n" + "(without dispensing solder paste).") + ) + param_grid.addWidget(self.z_travel_label, 2, 0) + param_grid.addWidget(self.z_travel_entry, 2, 1) # ############################################################################################################# # Dispense Frame @@ -1472,6 +1510,20 @@ class SolderUI: fr_grid.addWidget(self.frxy_label, 0, 0) fr_grid.addWidget(self.frxy_entry, 0, 1) + # Feedrate Rapids + self.frapids_lbl = FCLabel('%s:' % _("Feedrate Rapids")) + self.frapids_lbl.setToolTip( + _("Feedrate while moving as fast as possible.") + ) + + self.fr_rapids_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.fr_rapids_entry.set_range(0.0000, 10000.0000) + self.fr_rapids_entry.set_precision(self.decimals) + self.fr_rapids_entry.setSingleStep(0.1) + + fr_grid.addWidget(self.frapids_lbl, 2, 0) + fr_grid.addWidget(self.fr_rapids_entry, 2, 1) + # Feedrate Z self.frz_entry = FCDoubleSpinner(callback=self.confirmation_message) self.frz_entry.set_range(0.0000, 910000.0000) @@ -1483,8 +1535,8 @@ class SolderUI: _("Feedrate (speed) while moving vertically\n" "(on Z plane).") ) - fr_grid.addWidget(self.frz_label, 2, 0) - fr_grid.addWidget(self.frz_entry, 2, 1) + fr_grid.addWidget(self.frz_label, 4, 0) + fr_grid.addWidget(self.frz_entry, 4, 1) # Feedrate Z Dispense self.frz_dispense_entry = FCDoubleSpinner(callback=self.confirmation_message) @@ -1497,8 +1549,8 @@ class SolderUI: _("Feedrate (speed) while moving up vertically\n" "to Dispense position (on Z plane).") ) - fr_grid.addWidget(self.frz_dispense_label, 4, 0) - fr_grid.addWidget(self.frz_dispense_entry, 4, 1) + fr_grid.addWidget(self.frz_dispense_label, 6, 0) + fr_grid.addWidget(self.frz_dispense_entry, 6, 1) # ############################################################################################################# # Spindle Forward Frame @@ -1577,13 +1629,19 @@ class SolderUI: sp_rev_grid.addWidget(self.dwellrev_entry, 2, 1) # ############################################################################################################# - # Preprocessors Frame + # General Parameters Frame # ############################################################################################################# - pp_frame = FCFrame() - self.tools_box.addWidget(pp_frame) + self.gen_param_label = FCLabel('%s' % _("Common Parameters"), color='indigo', bold=True) + self.gen_param_label.setToolTip( + _("Parameters that are common for all tools.") + ) + self.tools_box.addWidget(self.gen_param_label) - pp_grid = GLay(v_spacing=5, h_spacing=3) - pp_frame.setLayout(pp_grid) + gen_par_frame = FCFrame() + self.tools_box.addWidget(gen_par_frame) + + gen_param_grid = GLay(v_spacing=5, h_spacing=3) + gen_par_frame.setLayout(gen_param_grid) pp_label = FCLabel('%s:' % _('Preprocessor')) pp_label.setToolTip( @@ -1591,8 +1649,8 @@ class SolderUI: ) self.pp_combo = FCComboBox() - pp_grid.addWidget(pp_label, 0, 0) - pp_grid.addWidget(self.pp_combo, 0, 1) + gen_param_grid.addWidget(pp_label, 0, 0) + gen_param_grid.addWidget(self.pp_combo, 0, 1) # ############################################################################################################# # Geometry Frame @@ -1702,7 +1760,7 @@ class SolderUI: GLay.set_common_column_size( [geo_grid, fr_grid, tc_grid, disp_grid, tool_grid, sp_fw_grid, sp_rev_grid, param_grid, cnc_grid, - pp_grid], 0) + gen_param_grid], 0) self.layout.addStretch(1) diff --git a/camlib.py b/camlib.py index a0507d3d..d7963710 100644 --- a/camlib.py +++ b/camlib.py @@ -6263,7 +6263,7 @@ class CNCjob(Geometry): return self.gcode, start_gcode - def generate_gcode_from_solderpaste_geo(self, **kwargs): + def generate_gcode_from_solderpaste_geo(self, is_first=False, **kwargs): """ Algorithm to generate from multitool Geometry. @@ -6290,6 +6290,7 @@ class CNCjob(Geometry): # this is the tool diameter, it is used as such to accommodate the preprocessor who need the tool diameter # given under the name 'toolC' + self.postdata['toolC'] = kwargs['tooldia'] self.postdata['z_start'] = kwargs['data']['tools_solderpaste_z_start'] self.postdata['z_dispense'] = kwargs['data']['tools_solderpaste_z_dispense'] @@ -6298,6 +6299,7 @@ class CNCjob(Geometry): 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['fr_rapids'] = kwargs['data']['tools_solderpaste_fr_rapids'] 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'] @@ -6306,8 +6308,6 @@ class CNCjob(Geometry): 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.options['tools_solderpaste_pp'] p = self.app.preprocessors[self.pp_solderpaste_name] @@ -6331,7 +6331,9 @@ class CNCjob(Geometry): storage.insert(geo_shape) # Initial G-Code - self.gcode = self.doformat(p.start_code) + self.gcode = '' + if is_first: + self.gcode += self.doformat(p.start_code) self.gcode += self.doformat(p.spindle_off_code) self.gcode += self.doformat(p.toolchange_code) diff --git a/defaults.py b/defaults.py index 8dc78ab6..61af3fec 100644 --- a/defaults.py +++ b/defaults.py @@ -658,6 +658,7 @@ class AppDefaults: "tools_solderpaste_z_toolchange": 1.0, "tools_solderpaste_xy_toolchange": "0.0, 0.0", "tools_solderpaste_frxy": 150, + "tools_solderpaste_fr_rapids": 1500, "tools_solderpaste_frz": 150, "tools_solderpaste_frz_dispense": 1.0, "tools_solderpaste_speedfwd": 300, diff --git a/preprocessors/Paste_1.py b/preprocessors/Paste_1.py index e295048d..c30698f0 100644 --- a/preprocessors/Paste_1.py +++ b/preprocessors/Paste_1.py @@ -17,31 +17,98 @@ class Paste_1(AppPreProcTools): def start_code(self, p): units = ' ' + str(p['units']).lower() - if isinstance(p['xy_toolchange'], str): - temp_val = p['xy_toolchange'].replace('[', '').replace(']', '') - coords_xy = [float(eval(a)) for a in temp_val.split(",") if a != ''] - else: - coords_xy = p['xy_toolchange'] - gcode = '(This preprocessor is to be used only with the SolderPaste Plugin.)\n\n' + gcode = '(This preprocessor is used only with the SolderPaste Plugin and with MACH3-like controllers.)\n\n' xmin = '%.*f' % (p.coords_decimals, p['obj_options']['xmin']) xmax = '%.*f' % (p.coords_decimals, p['obj_options']['xmax']) ymin = '%.*f' % (p.coords_decimals, p['obj_options']['ymin']) ymax = '%.*f' % (p.coords_decimals, p['obj_options']['ymax']) - gcode += '(TOOL DIAMETER: ' + str(p['obj_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 += '\n(TOOLS DIAMETER: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + units + ')\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' + gcode += '\n(MARGIN: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Margin: %s' % str( + val['data']["tools_solderpaste_margin"]) + ')\n' - gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0], - p.decimals, coords_xy[1]) + units + ')\n' + gcode += '\n(FEEDRATE XY: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate XY: %s' % \ + str(val['data']["tools_solderpaste_frxy"]) + units + '/min' + ')\n' + + gcode += '\n(FEEDRATE RAPIDS: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \ + str(val['data']["tools_solderpaste_fr_rapids"]) + units + '/min' + ')\n' + + gcode += '\n(FEEDRATE Z: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Z: %s' % \ + str(val['data']["tools_solderpaste_frz"]) + units + '/min' + ')\n' + + gcode += '\n(FEEDRATE Z_DISPENSE: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Z_Dispense: %s' % \ + str(val['data']["tools_solderpaste_frz_dispense"]) + units + '/min' + ')\n' + + gcode += '\n(Z_DISPENSE START: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Dispense_Start: %s' % str( + val['data']["tools_solderpaste_z_start"]) + units + ')\n' + + gcode += '\n(Z_DISPENSE: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Dispense: %s' % str( + val['data']["tools_solderpaste_z_dispense"]) + units + ')\n' + + gcode += '\n(Z_DISPENSE STOP: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Dispense_Stop: %s' % str( + val['data']["tools_solderpaste_z_stop"]) + units + ')\n' + + gcode += '\n(Z_TRAVEL: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Travel: %s' % str( + val['data']["tools_solderpaste_z_travel"]) + units + ')\n' + + gcode += '\n(Z_TOOLCHANGE: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Toolchange: %s' % str( + val['data']["tools_solderpaste_z_toolchange"]) + units + ')\n' + + gcode += '\n(XY_TOOLCHANGE: )\n' + for tool, val in p['tools'].items(): + xy_tc_coords = val['data']["tools_solderpaste_xy_toolchange"] + if isinstance(xy_tc_coords, str): + temp_val = xy_tc_coords.replace('[', '').replace(']', '') + coords_xy = [float(eval(a)) for a in temp_val.split(",") if a != ''] + else: + coords_xy = xy_tc_coords + + xy_coords_formatted = "%.*f, %.*f" % (p.decimals, coords_xy[0], p.decimals, coords_xy[1]) + gcode += '(Tool: %s -> ' % str(tool) + 'X,Y Toolchange: %s' % xy_coords_formatted + units + ')\n' + + gcode += '\n(Spindle Speed FWD: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Spindle Speed FWD: %s RPM' % str( + val['data']["tools_solderpaste_speedfwd"]) + units + ')\n' + + gcode += '\n(Dwell FWD: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Dwell FWD: %s' % str( + val['data']["tools_solderpaste_dwellfwd"]) + units + ')\n' + + gcode += '\n(Spindle Speed REV: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Spindle Speed REV: %s RPM' % str( + val['data']["tools_solderpaste_speedrev"]) + units + ')\n' + + gcode += '\n(Dwell REV: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Dwell REV: %s' % str( + val['data']["tools_solderpaste_dwellrev"]) + units + ')\n' if 'Paste' in p.pp_solderpaste_name: gcode += '(Preprocessor SolderPaste Dispensing Geometry: ' + str(p.pp_solderpaste_name) + ')\n' + '\n' @@ -49,29 +116,27 @@ class Paste_1(AppPreProcTools): 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 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 lift_code(self, p): - return 'G00 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + \ + ' F%s' % str(p['fr_rapids']) def down_z_start_code(self, p): - return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_start'])) + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_start'])) + ' F%s' % str(p['frz']) def lift_z_dispense_code(self, p): - return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_dispense'])) + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_dispense'])) + \ + ' F%s' % str(p['frz_dispense']) def down_z_stop_code(self, p): - return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_stop'])) + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_stop'])) + ' F%s' % str(p['frz']) def toolchange_code(self, p): + fr_rapids = float(p['fr_rapids']) z_toolchange = float(p['z_toolchange']) if isinstance(p['xy_toolchange'], str): @@ -91,30 +156,34 @@ class Paste_1(AppPreProcTools): if toolchangexy is not None: gcode = """ -G00 Z{z_toolchange} -G00 X{x_toolchange} Y{y_toolchange} +(Toolchange: START) +G01 Z{z_toolchange} F{fr_rapids} +G01 X{x_toolchange} Y{y_toolchange} F{fr_rapids} T{tool} M6 (MSG, Change to Tool with Nozzle Dia = {toolC}) M0 -G00 Z{z_toolchange} +G01 Z{z_toolchange} F{fr_rapids} """.format(x_toolchange=self.coordinate_format % (p.coords_decimals, x_toolchange), y_toolchange=self.coordinate_format % (p.coords_decimals, y_toolchange), z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange), tool=int(int(p.tool)), - toolC=toolC_formatted) + toolC=toolC_formatted, + fr_rapids=fr_rapids) else: gcode = """ -G00 Z{z_toolchange} +(Toolchange: START) +G01 Z{z_toolchange} F{fr_rapids} T{tool} M6 (MSG, Change to Tool with Nozzle Dia = {toolC}) M0 -G00 Z{z_toolchange} +G01 Z{z_toolchange} F{fr_rapids} """.format(z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange), tool=int(int(p.tool)), - toolC=toolC_formatted) + toolC=toolC_formatted, + fr_rapids=fr_rapids) return gcode @@ -135,22 +204,25 @@ G00 Z{z_toolchange} (p.coords_decimals, x_pos, p.coords_decimals, y_pos) def rapid_code(self, p): - return ('G00 ' + self.position_code(p)).format(**p) + '\nG00 Z' + \ - self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + return ('G01 ' + self.position_code(p)).format(**p) + ' F%s' % str(p['fr_rapids']) + \ + '\nG01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + \ + ' F%s' % str(p['fr_rapids']) def linear_code(self, p): - return ('G01 ' + self.position_code(p)).format(**p) + return ('G01 ' + self.position_code(p)).format(**p) + ' F' + \ + str(self.feedrate_format % (p.fr_decimals, float(p['frxy']))) def end_code(self, p): coords_xy = [float(eval(a)) for a in p['xy_end'].split(",") if a != ''] - gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, float(p['z_toolchange'])) + "\n") + gcode = ('G01 Z' + self.feedrate_format % (p.fr_decimals, float(p['z_toolchange'])) + + 'F%s' % str(p['fr_rapids']) + "\n") if coords_xy and coords_xy != '': - gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" + gcode += 'G01 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + ' F%s' % str(p['fr_rapids']) + "\n" return gcode def feedrate_xy_code(self, p): - return 'G01 F' + str(self.feedrate_format % (p.fr_decimals, float(p['frxy']))) + return '' def z_feedrate_code(self, p): return 'G01 F' + str(self.feedrate_format % (p.fr_decimals, float(p['frz']))) diff --git a/preprocessors/Paste_GRBL.py b/preprocessors/Paste_GRBL.py new file mode 100644 index 00000000..38e0b8a3 --- /dev/null +++ b/preprocessors/Paste_GRBL.py @@ -0,0 +1,253 @@ +# ########################################################## +# FlatCAM: 2D Post-processing for Manufacturing # +# http://flatcam.org # +# File Author: Marius Adrian Stanciu (c) # +# Date: 3/10/2019 # +# MIT Licence # +# ########################################################## + +from appPreProcessor import * + + +class Paste_GRBL(AppPreProcTools): + + include_header = True + coordinate_format = "%.*f" + feedrate_format = '%.*f' + + def start_code(self, p): + units = ' ' + str(p['units']).lower() + gcode = '(This preprocessor is used only with the SolderPaste Plugin and with MACH3-like controllers.)\n\n' + + xmin = '%.*f' % (p.coords_decimals, p['obj_options']['xmin']) + xmax = '%.*f' % (p.coords_decimals, p['obj_options']['xmax']) + ymin = '%.*f' % (p.coords_decimals, p['obj_options']['ymin']) + ymax = '%.*f' % (p.coords_decimals, p['obj_options']['ymax']) + + gcode += '\n(TOOLS DIAMETER: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + units + ')\n' + + gcode += '\n(MARGIN: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Margin: %s' % str( + val['data']["tools_solderpaste_margin"]) + ')\n' + + gcode += '\n(FEEDRATE XY: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate XY: %s' % \ + str(val['data']["tools_solderpaste_frxy"]) + units + '/min' + ')\n' + + gcode += '\n(FEEDRATE RAPIDS: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \ + str(val['data']["tools_solderpaste_fr_rapids"]) + units + '/min' + ')\n' + + gcode += '\n(FEEDRATE Z: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Z: %s' % \ + str(val['data']["tools_solderpaste_frz"]) + units + '/min' + ')\n' + + gcode += '\n(FEEDRATE Z_DISPENSE: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Z_Dispense: %s' % \ + str(val['data']["tools_solderpaste_frz_dispense"]) + units + '/min' + ')\n' + + gcode += '\n(Z_DISPENSE START: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Dispense_Start: %s' % str( + val['data']["tools_solderpaste_z_start"]) + units + ')\n' + + gcode += '\n(Z_DISPENSE: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Dispense: %s' % str( + val['data']["tools_solderpaste_z_dispense"]) + units + ')\n' + + gcode += '\n(Z_DISPENSE STOP: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Dispense_Stop: %s' % str( + val['data']["tools_solderpaste_z_stop"]) + units + ')\n' + + gcode += '\n(Z_TRAVEL: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Travel: %s' % str( + val['data']["tools_solderpaste_z_travel"]) + units + ')\n' + + gcode += '\n(Z_TOOLCHANGE: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Z_Toolchange: %s' % str( + val['data']["tools_solderpaste_z_toolchange"]) + units + ')\n' + + gcode += '\n(XY_TOOLCHANGE: )\n' + for tool, val in p['tools'].items(): + xy_tc_coords = val['data']["tools_solderpaste_xy_toolchange"] + if isinstance(xy_tc_coords, str): + temp_val = xy_tc_coords.replace('[', '').replace(']', '') + coords_xy = [float(eval(a)) for a in temp_val.split(",") if a != ''] + else: + coords_xy = xy_tc_coords + + xy_coords_formatted = "%.*f, %.*f" % (p.decimals, coords_xy[0], p.decimals, coords_xy[1]) + gcode += '(Tool: %s -> ' % str(tool) + 'X,Y Toolchange: %s' % xy_coords_formatted + units + ')\n' + + gcode += '\n(Spindle Speed FWD: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Spindle Speed FWD: %s RPM' % str( + val['data']["tools_solderpaste_speedfwd"]) + units + ')\n' + + gcode += '\n(Dwell FWD: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Dwell FWD: %s' % str( + val['data']["tools_solderpaste_dwellfwd"]) + units + ')\n' + + gcode += '\n(Spindle Speed REV: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Spindle Speed REV: %s RPM' % str( + val['data']["tools_solderpaste_speedrev"]) + units + ')\n' + + gcode += '\n(Dwell REV: )\n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Dwell REV: %s' % str( + val['data']["tools_solderpaste_dwellrev"]) + units + ')\n' + + if 'Paste' in p.pp_solderpaste_name: + gcode += '(Preprocessor 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 += ('G20\n' if p.units.upper() == 'IN' else 'G21\n') + gcode += 'G90\n' + gcode += 'G17\n' + gcode += 'G94\n' + return gcode + + def lift_code(self, p): + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + \ + ' F%s' % str(p['fr_rapids']) + + def down_z_start_code(self, p): + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_start'])) + ' F%s' % str(p['frz']) + + def lift_z_dispense_code(self, p): + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_dispense'])) + \ + ' F%s' % str(p['frz_dispense']) + + def down_z_stop_code(self, p): + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_stop'])) + ' F%s' % str(p['frz']) + + def toolchange_code(self, p): + fr_rapids = float(p['fr_rapids']) + z_toolchange = float(p['z_toolchange']) + + if isinstance(p['xy_toolchange'], str): + temp_val = p['xy_toolchange'].replace('[', '').replace(']', '') + toolchangexy = [float(eval(a)) for a in temp_val.split(",") if a != ''] + else: + toolchangexy = p['xy_toolchange'] + + if toolchangexy is not None: + x_toolchange = toolchangexy[0] + y_toolchange = toolchangexy[1] + else: + x_toolchange = 0.0 + y_toolchange = 0.0 + + toolC_formatted = '%.*f' % (p.decimals, float(p['toolC'])) + + if toolchangexy is not None: + gcode = """ +(Toolchange: START) +G01 Z{z_toolchange} F{fr_rapids} +G01 X{x_toolchange} Y{y_toolchange} F{fr_rapids} +T{tool} +(MSG, Change to Tool with Nozzle Dia = {toolC}) +M0 +G01 Z{z_toolchange} F{fr_rapids} +""".format(x_toolchange=self.coordinate_format % (p.coords_decimals, x_toolchange), + y_toolchange=self.coordinate_format % (p.coords_decimals, y_toolchange), + z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange), + tool=int(int(p.tool)), + toolC=toolC_formatted, + fr_rapids=fr_rapids) + + else: + gcode = """ +(Toolchange: START) +G01 Z{z_toolchange} F{fr_rapids} +T{tool} +(MSG, Change to Tool with Nozzle Dia = {toolC}) +M0 +G01 Z{z_toolchange} F{fr_rapids} +""".format(z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange), + tool=int(int(p.tool)), + toolC=toolC_formatted, + fr_rapids=fr_rapids) + + return gcode + + def position_code(self, p): + # formula for skewing on x for example is: + # x_fin = x_init + y_init/slope where slope = p._bed_limit_y / p._bed_skew_x (a.k.a tangent) + if p._bed_skew_x == 0: + x_pos = p.x + p._bed_offset_x + else: + x_pos = (p.x + p._bed_offset_x) + ((p.y / p._bed_limit_y) * p._bed_skew_x) + + if p._bed_skew_y == 0: + y_pos = p.y + p._bed_offset_y + else: + y_pos = (p.y + p._bed_offset_y) + ((p.x / p._bed_limit_x) * p._bed_skew_y) + + return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \ + (p.coords_decimals, x_pos, p.coords_decimals, y_pos) + + def rapid_code(self, p): + return ('G01 ' + self.position_code(p)).format(**p) + ' F%s' % str(p['fr_rapids']) + \ + '\nG01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + \ + ' F%s' % str(p['fr_rapids']) + + def linear_code(self, p): + return ('G01 ' + self.position_code(p)).format(**p) + ' F' + \ + str(self.feedrate_format % (p.fr_decimals, float(p['frxy']))) + + def end_code(self, p): + coords_xy = [float(eval(a)) for a in p['xy_end'].split(",") if a != ''] + gcode = ('G01 Z' + self.feedrate_format % (p.fr_decimals, float(p['z_toolchange'])) + + 'F%s' % str(p['fr_rapids']) + "\n") + + if coords_xy and coords_xy != '': + gcode += 'G01 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + ' F%s' % str(p['fr_rapids']) + "\n" + return gcode + + def feedrate_xy_code(self, p): + return '' + + def z_feedrate_code(self, p): + return 'G01 F' + str(self.feedrate_format % (p.fr_decimals, float(p['frz']))) + + 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(float(p['speedfwd'])) + else: + return 'M03' + + def spindle_rev_code(self, p): + if p.spindlespeed: + return 'M04 S' + str(float(p['speedrev'])) + else: + return 'M04' + + 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'])) \ No newline at end of file diff --git a/preprocessors/Paste_Marlin.py b/preprocessors/Paste_Marlin.py new file mode 100644 index 00000000..ebc56def --- /dev/null +++ b/preprocessors/Paste_Marlin.py @@ -0,0 +1,252 @@ +# ########################################################## +# FlatCAM: 2D Post-processing for Manufacturing # +# http://flatcam.org # +# File Author: Marius Adrian Stanciu (c) # +# Date: 3/10/2019 # +# MIT Licence # +# ########################################################## + +from appPreProcessor import * + + +class Paste_Marlin(AppPreProcTools): + + include_header = True + coordinate_format = "%.*f" + feedrate_format = '%.*f' + + def start_code(self, p): + units = ' ' + str(p['units']).lower() + gcode = '(This preprocessor is used only with the SolderPaste Plugin and with MARLIN-like controllers.)\n\n' + + xmin = '%.*f' % (p.coords_decimals, p['obj_options']['xmin']) + xmax = '%.*f' % (p.coords_decimals, p['obj_options']['xmax']) + ymin = '%.*f' % (p.coords_decimals, p['obj_options']['ymin']) + ymax = '%.*f' % (p.coords_decimals, p['obj_options']['ymax']) + + gcode += '\n;TOOLS DIAMETER: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + units + '\n' + + gcode += '\nMARGIN: ;\n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Margin: %s' % str( + val['data']["tools_solderpaste_margin"]) + '\n' + + gcode += '\n;FEEDRATE XY: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate XY: %s' % \ + str(val['data']["tools_solderpaste_frxy"]) + units + '/min' + '\n' + + gcode += '\n;FEEDRATE RAPIDS: \n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \ + str(val['data']["tools_solderpaste_fr_rapids"]) + units + '/min' + '\n' + + gcode += '\n;FEEDRATE Z: \n' + for tool, val in p['tools'].items(): + gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Z: %s' % \ + str(val['data']["tools_solderpaste_frz"]) + units + '/min' + '\n' + + gcode += '\n;FEEDRATE Z_DISPENSE: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate Z_Dispense: %s' % \ + str(val['data']["tools_solderpaste_frz_dispense"]) + units + '/min' + '\n' + + gcode += '\n;Z_DISPENSE START: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Z_Dispense_Start: %s' % str( + val['data']["tools_solderpaste_z_start"]) + units + '\n' + + gcode += '\n;Z_DISPENSE: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Z_Dispense: %s' % str( + val['data']["tools_solderpaste_z_dispense"]) + units + '\n' + + gcode += '\n;Z_DISPENSE STOP: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Z_Dispense_Stop: %s' % str( + val['data']["tools_solderpaste_z_stop"]) + units + '\n' + + gcode += '\n;Z_TRAVEL: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Z_Travel: %s' % str( + val['data']["tools_solderpaste_z_travel"]) + units + '\n' + + gcode += '\n;Z_TOOLCHANGE: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Z_Toolchange: %s' % str( + val['data']["tools_solderpaste_z_toolchange"]) + units + '\n' + + gcode += '\n;XY_TOOLCHANGE: \n' + for tool, val in p['tools'].items(): + xy_tc_coords = val['data']["tools_solderpaste_xy_toolchange"] + if isinstance(xy_tc_coords, str): + temp_val = xy_tc_coords.replace('[', '').replace(']', '') + coords_xy = [float(eval(a)) for a in temp_val.split(",") if a != ''] + else: + coords_xy = xy_tc_coords + + xy_coords_formatted = "%.*f, %.*f" % (p.decimals, coords_xy[0], p.decimals, coords_xy[1]) + gcode += ';Tool: %s -> ' % str(tool) + 'X,Y Toolchange: %s' % xy_coords_formatted + units + '\n' + + gcode += '\n;Spindle Speed FWD: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Spindle Speed FWD: %s RPM' % str( + val['data']["tools_solderpaste_speedfwd"]) + units + '\n' + + gcode += '\n;Dwell FWD: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Dwell FWD: %s' % str( + val['data']["tools_solderpaste_dwellfwd"]) + units + '\n' + + gcode += '\n;Spindle Speed REV: \n' + for tool, val in p['tools'].items(): + gcode += ';Tool: %s -> ' % str(tool) + 'Spindle Speed REV: %s RPM' % str( + val['data']["tools_solderpaste_speedrev"]) + units + '\n' + + gcode += '\n;Dwell REV: \n' + for tool, val in p['tools'].items(): + gcode += 'Tool: %s -> ' % str(tool) + 'Dwell REV: %s' % str( + val['data']["tools_solderpaste_dwellrev"]) + units + '\n' + + if 'Paste' in p.pp_solderpaste_name: + gcode += ';Preprocessor 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 += ('G20\n' if p.units.upper() == 'IN' else 'G21\n') + return gcode + + def lift_code(self, p): + return 'G1 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + \ + ' F%s' % str(p['fr_rapids']) + + def down_z_start_code(self, p): + return 'G1 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_start'])) + ' F%s' % str(p['frz']) + + def lift_z_dispense_code(self, p): + return 'G01 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_dispense'])) + \ + ' F%s' % str(p['frz_dispense']) + + def down_z_stop_code(self, p): + return 'G1 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_stop'])) + ' F%s' % str(p['frz']) + + def toolchange_code(self, p): + fr_rapids = float(p['fr_rapids']) + z_toolchange = float(p['z_toolchange']) + + if isinstance(p['xy_toolchange'], str): + temp_val = p['xy_toolchange'].replace('[', '').replace(']', '') + toolchangexy = [float(eval(a)) for a in temp_val.split(",") if a != ''] + else: + toolchangexy = p['xy_toolchange'] + + if toolchangexy is not None: + x_toolchange = toolchangexy[0] + y_toolchange = toolchangexy[1] + else: + x_toolchange = 0.0 + y_toolchange = 0.0 + + toolC_formatted = '%.*f' % (p.decimals, float(p['toolC'])) + + if toolchangexy is not None: + gcode = """ +;Toolchange: START +G1 Z{z_toolchange} F{fr_rapids} +G1 X{x_toolchange} Y{y_toolchange} F{fr_rapids} +T{tool} +M6 +;MSG, Change to Tool with Nozzle Dia = {toolC} +M0 +G1 Z{z_toolchange} F{fr_rapids} +""".format(x_toolchange=self.coordinate_format % (p.coords_decimals, x_toolchange), + y_toolchange=self.coordinate_format % (p.coords_decimals, y_toolchange), + z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange), + tool=int(int(p.tool)), + toolC=toolC_formatted, + fr_rapids=fr_rapids) + + else: + gcode = """ +;Toolchange: START +G1 Z{z_toolchange} F{fr_rapids} +T{tool} +M6 +;MSG, Change to Tool with Nozzle Dia = {toolC} +M0 +G1 Z{z_toolchange} F{fr_rapids} +""".format(z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange), + tool=int(int(p.tool)), + toolC=toolC_formatted, + fr_rapids=fr_rapids) + + return gcode + + def position_code(self, p): + # formula for skewing on x for example is: + # x_fin = x_init + y_init/slope where slope = p._bed_limit_y / p._bed_skew_x (a.k.a tangent) + if p._bed_skew_x == 0: + x_pos = p.x + p._bed_offset_x + else: + x_pos = (p.x + p._bed_offset_x) + ((p.y / p._bed_limit_y) * p._bed_skew_x) + + if p._bed_skew_y == 0: + y_pos = p.y + p._bed_offset_y + else: + y_pos = (p.y + p._bed_offset_y) + ((p.x / p._bed_limit_x) * p._bed_skew_y) + + return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \ + (p.coords_decimals, x_pos, p.coords_decimals, y_pos) + + def rapid_code(self, p): + return ('G1 ' + self.position_code(p)).format(**p) + ' F%s' % str(p['fr_rapids']) + \ + '\nG1 Z' + self.coordinate_format % (p.coords_decimals, float(p['z_travel'])) + \ + ' F%s' % str(p['fr_rapids']) + + def linear_code(self, p): + return ('G1 ' + self.position_code(p)).format(**p) + ' F' + \ + str(self.feedrate_format % (p.fr_decimals, float(p['frxy']))) + + def end_code(self, p): + coords_xy = [float(eval(a)) for a in p['xy_end'].split(",") if a != ''] + gcode = ('G1 Z' + self.feedrate_format % (p.fr_decimals, float(p['z_toolchange'])) + + 'F%s' % str(p['fr_rapids']) + "\n") + + if coords_xy and coords_xy != '': + gcode += 'G1 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + ' F%s' % str(p['fr_rapids']) + "\n" + return gcode + + def feedrate_xy_code(self, p): + return '' + + def z_feedrate_code(self, p): + return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, float(p['frz']))) + + def feedrate_z_dispense_code(self, p): + return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, float(p['frz_dispense']))) + + def spindle_fwd_code(self, p): + if p.spindlespeed: + return 'M3 S' + str(float(p['speedfwd'])) + else: + return 'M3' + + def spindle_rev_code(self, p): + if p.spindlespeed: + return 'M4 S' + str(float(p['speedrev'])) + else: + return 'M4' + + def spindle_off_code(self, p): + return 'M5' + + 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']))