Bläddra i källkod

- added a new preprocessor for using laser on a Marlin 3D printer named 'Marlin_laser'
- modified the Geometry UI when using laser preprocessors

Marius Stanciu 6 år sedan
förälder
incheckning
9911402c95
9 ändrade filer med 336 tillägg och 107 borttagningar
  1. 2 2
      FlatCAMApp.py
  2. 115 8
      FlatCAMObj.py
  3. 6 1
      README.md
  4. 35 40
      camlib.py
  5. 38 42
      flatcamGUI/ObjectUI.py
  6. 10 10
      flatcamGUI/PreferencesUI.py
  7. 2 2
      preprocessors/Marlin.py
  8. 122 0
      preprocessors/Marlin_laser.py
  9. 6 2
      preprocessors/grbl_laser.py

+ 2 - 2
FlatCAMApp.py

@@ -1291,7 +1291,7 @@ class App(QtCore.QObject):
             "excellon_drillz": self.ui.excellon_defaults_form.excellon_opt_group.cutz_entry,
             "excellon_drillz": self.ui.excellon_defaults_form.excellon_opt_group.cutz_entry,
             "excellon_travelz": self.ui.excellon_defaults_form.excellon_opt_group.travelz_entry,
             "excellon_travelz": self.ui.excellon_defaults_form.excellon_opt_group.travelz_entry,
             "excellon_endz": self.ui.excellon_defaults_form.excellon_opt_group.eendz_entry,
             "excellon_endz": self.ui.excellon_defaults_form.excellon_opt_group.eendz_entry,
-            "excellon_feedrate": self.ui.excellon_defaults_form.excellon_opt_group.feedrate_entry,
+            "excellon_feedrate": self.ui.excellon_defaults_form.excellon_opt_group.feedrate_z_entry,
             "excellon_spindlespeed": self.ui.excellon_defaults_form.excellon_opt_group.spindlespeed_entry,
             "excellon_spindlespeed": self.ui.excellon_defaults_form.excellon_opt_group.spindlespeed_entry,
             "excellon_dwell": self.ui.excellon_defaults_form.excellon_opt_group.dwell_cb,
             "excellon_dwell": self.ui.excellon_defaults_form.excellon_opt_group.dwell_cb,
             "excellon_dwelltime": self.ui.excellon_defaults_form.excellon_opt_group.dwelltime_entry,
             "excellon_dwelltime": self.ui.excellon_defaults_form.excellon_opt_group.dwelltime_entry,
@@ -1361,7 +1361,7 @@ class App(QtCore.QObject):
             "geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry,
             "geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry,
             "geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry,
             "geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry,
             "geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry,
             "geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry,
-            "geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.cncplunge_entry,
+            "geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.feedrate_z_entry,
             "geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry,
             "geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry,
             "geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb,
             "geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb,
             "geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry,
             "geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry,

+ 115 - 8
FlatCAMObj.py

@@ -2804,7 +2804,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             "solid": self.ui.solid_cb,
             "solid": self.ui.solid_cb,
             "drillz": self.ui.cutz_entry,
             "drillz": self.ui.cutz_entry,
             "travelz": self.ui.travelz_entry,
             "travelz": self.ui.travelz_entry,
-            "feedrate": self.ui.feedrate_entry,
+            "feedrate": self.ui.feedrate_z_entry,
             "feedrate_rapid": self.ui.feedrate_rapid_entry,
             "feedrate_rapid": self.ui.feedrate_rapid_entry,
             "tooldia": self.ui.tooldia_entry,
             "tooldia": self.ui.tooldia_entry,
             "slot_tooldia": self.ui.slot_tooldia_entry,
             "slot_tooldia": self.ui.slot_tooldia_entry,
@@ -2978,12 +2978,14 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             self.ui.mill_type_radio.show()
             self.ui.mill_type_radio.show()
             self.ui.mill_dia_label.show()
             self.ui.mill_dia_label.show()
             self.ui.mill_dia_entry.show()
             self.ui.mill_dia_entry.show()
-            self.ui.mpass_cb.show()
-            self.ui.maxdepth_entry.show()
             self.ui.frxylabel.show()
             self.ui.frxylabel.show()
             self.ui.xyfeedrate_entry.show()
             self.ui.xyfeedrate_entry.show()
             self.ui.extracut_cb.show()
             self.ui.extracut_cb.show()
             self.ui.e_cut_entry.show()
             self.ui.e_cut_entry.show()
+
+            if 'laser' not in self.ui.pp_excellon_name_cb.get_value().lower():
+                self.ui.mpass_cb.show()
+                self.ui.maxdepth_entry.show()
         else:
         else:
             self.ui.mill_type_label.hide()
             self.ui.mill_type_label.hide()
             self.ui.mill_type_radio.hide()
             self.ui.mill_type_radio.hide()
@@ -3464,6 +3466,59 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             self.ui.feedrate_rapid_label.hide()
             self.ui.feedrate_rapid_label.hide()
             self.ui.feedrate_rapid_entry.hide()
             self.ui.feedrate_rapid_entry.hide()
 
 
+        if 'laser' in current_pp.lower():
+            self.ui.cutzlabel.hide()
+            self.ui.cutz_entry.hide()
+            try:
+                self.ui.mpass_cb.hide()
+                self.ui.maxdepth_entry.hide()
+            except AttributeError:
+                pass
+
+            self.ui.travelzlabel.setText('%s:' % _("Focus Z"))
+
+            try:
+                self.ui.frzlabel.hide()
+                self.ui.feedrate_z_entry.hide()
+            except AttributeError:
+                pass
+            self.ui.dwell_cb.hide()
+            self.ui.dwelltime_entry.hide()
+
+            self.ui.spindle_label.setText('%s:' % _("Laser Power"))
+
+            try:
+                self.ui.tool_offset_label.hide()
+                self.ui.offset_entry.hide()
+            except AttributeError:
+                pass
+        else:
+            self.ui.cutzlabel.show()
+            self.ui.cutz_entry.show()
+            try:
+                self.ui.mpass_cb.show()
+                self.ui.maxdepth_entry.show()
+            except AttributeError:
+                pass
+
+            self.ui.travelzlabel.setText('%s:' % _('Travel Z'))
+
+            try:
+                self.ui.frzlabel.show()
+                self.ui.feedrate_z_entry.show()
+            except AttributeError:
+                pass
+            self.ui.dwell_cb.show()
+            self.ui.dwelltime_entry.show()
+
+            self.ui.spindle_label.setText('%s:' % _('Spindle speed'))
+
+            try:
+                self.ui.tool_offset_lbl.show()
+                self.ui.offset_entry.show()
+            except AttributeError:
+                pass
+
     def on_create_cncjob_button_click(self, *args):
     def on_create_cncjob_button_click(self, *args):
         self.app.report_usage("excellon_on_create_cncjob_button")
         self.app.report_usage("excellon_on_create_cncjob_button")
         self.read_form()
         self.read_form()
@@ -4026,7 +4081,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             "vtipangle": self.ui.tipangle_entry,
             "vtipangle": self.ui.tipangle_entry,
             "travelz": self.ui.travelz_entry,
             "travelz": self.ui.travelz_entry,
             "feedrate": self.ui.cncfeedrate_entry,
             "feedrate": self.ui.cncfeedrate_entry,
-            "feedrate_z": self.ui.cncplunge_entry,
+            "feedrate_z": self.ui.feedrate_z_entry,
             "feedrate_rapid": self.ui.cncfeedrate_rapid_entry,
             "feedrate_rapid": self.ui.cncfeedrate_rapid_entry,
             "spindlespeed": self.ui.cncspindlespeed_entry,
             "spindlespeed": self.ui.cncspindlespeed_entry,
             "dwell": self.ui.dwell_cb,
             "dwell": self.ui.dwell_cb,
@@ -5178,6 +5233,59 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             self.ui.fr_rapidlabel.hide()
             self.ui.fr_rapidlabel.hide()
             self.ui.cncfeedrate_rapid_entry.hide()
             self.ui.cncfeedrate_rapid_entry.hide()
 
 
+        if 'laser' in current_pp.lower():
+            self.ui.cutzlabel.hide()
+            self.ui.cutz_entry.hide()
+            try:
+                self.ui.mpass_cb.hide()
+                self.ui.maxdepth_entry.hide()
+            except AttributeError:
+                pass
+
+            self.ui.travelzlabel.setText('%s:' % _("Focus Z"))
+
+            try:
+                self.ui.frzlabel.hide()
+                self.ui.feedrate_z_entry.hide()
+            except AttributeError:
+                pass
+            self.ui.dwell_cb.hide()
+            self.ui.dwelltime_entry.hide()
+
+            self.ui.spindle_label.setText('%s:' % _("Laser Power"))
+
+            try:
+                self.ui.tool_offset_label.hide()
+                self.ui.offset_entry.hide()
+            except AttributeError:
+                pass
+        else:
+            self.ui.cutzlabel.show()
+            self.ui.cutz_entry.show()
+            try:
+                self.ui.mpass_cb.show()
+                self.ui.maxdepth_entry.show()
+            except AttributeError:
+                pass
+
+            self.ui.travelzlabel.setText('%s:' % _('Travel Z'))
+
+            try:
+                self.ui.frzlabel.show()
+                self.ui.feedrate_z_entry.show()
+            except AttributeError:
+                pass
+            self.ui.dwell_cb.show()
+            self.ui.dwelltime_entry.show()
+
+            self.ui.spindle_label.setText('%s:' % _('Spindle speed'))
+
+            try:
+                self.ui.tool_offset_lbl.show()
+                self.ui.offset_entry.show()
+            except AttributeError:
+                pass
+
     def on_generatecnc_button_click(self, *args):
     def on_generatecnc_button_click(self, *args):
         log.debug("Generating CNCJob from Geometry ...")
         log.debug("Generating CNCJob from Geometry ...")
         self.app.report_usage("geometry_on_generatecnc_button")
         self.app.report_usage("geometry_on_generatecnc_button")
@@ -6783,7 +6891,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         try:
         try:
             for key in self.cnc_tools:
             for key in self.cnc_tools:
                 ppg = self.cnc_tools[key]['data']['ppname_g']
                 ppg = self.cnc_tools[key]['data']['ppname_g']
-                if ppg == 'marlin' or ppg == 'Repetier':
+                if 'marlin' in ppg.lower() or 'repetier' in ppg.lower() :
                     marlin = True
                     marlin = True
                     break
                     break
                 if ppg == 'hpgl':
                 if ppg == 'hpgl':
@@ -6797,7 +6905,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
             pass
             pass
 
 
         try:
         try:
-            if self.options['ppname_e'] == 'marlin' or self.options['ppname_e'] == 'Repetier':
+            if 'marlin' in self.options['ppname_e'].lower() or 'repetier' in self.options['ppname_e'].lower():
                 marlin = True
                 marlin = True
         except KeyError:
         except KeyError:
             # log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e))
             # log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e))
@@ -7150,8 +7258,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
                                                  _("The used preprocessor file has to have in it's name: "
                                                  _("The used preprocessor file has to have in it's name: "
                                                    "'toolchange_custom'"))
                                                    "'toolchange_custom'"))
             except KeyError:
             except KeyError:
-                self.app.inform.emit('[ERROR] %s' %
-                                     _("There is no preprocessor file."))
+                self.app.inform.emit('[ERROR] %s' % _("There is no preprocessor file."))
 
 
     def get_gcode(self, preamble='', postamble=''):
     def get_gcode(self, preamble='', postamble=''):
         # we need this to be able get_gcode separatelly for shell command export_gcode
         # we need this to be able get_gcode separatelly for shell command export_gcode

+ 6 - 1
README.md

@@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing.
 
 
 =================================================
 =================================================
 
 
+8.02.2020
+
+- added a new preprocessor for using laser on a Marlin 3D printer named 'Marlin_laser'
+- modified the Geometry UI when using laser preprocessors
+
 5.02.2020
 5.02.2020
 
 
 - Modified the Distance Tool such that the Measure button can't be clicked while measuring is in progress
 - Modified the Distance Tool such that the Measure button can't be clicked while measuring is in progress
@@ -583,7 +588,7 @@ CAD program, and create G-Code for Isolation routing.
 
 
 16.11.2019
 16.11.2019
 
 
-- fixed issue #341 that affected both postprocessors that have inlined feedrate: marlin and repetier. THe used feedrate was the Feedrate X-Y and instead had to be Feedrate Z.
+- fixed issue #341 that affected both postprocessors that have inlined feedrate: marlin and repetier. The used feedrate was the Feedrate X-Y and instead had to be Feedrate Z.
 
 
 15.11.2019
 15.11.2019
 
 

+ 35 - 40
camlib.py

@@ -3299,7 +3299,7 @@ class CNCjob(Geometry):
 
 
         # ## Iterate over geometry paths getting the nearest each time.
         # ## Iterate over geometry paths getting the nearest each time.
         log.debug("Starting G-Code...")
         log.debug("Starting G-Code...")
-        self.app.inform.emit(_("Starting G-Code..."))
+        self.app.inform.emit('%s...' % _("Starting G-Code"))
 
 
         path_count = 0
         path_count = 0
         current_pt = (0, 0)
         current_pt = (0, 0)
@@ -3381,12 +3381,9 @@ class CNCjob(Geometry):
         self.gcode += self.doformat(p.spindle_stop_code)
         self.gcode += self.doformat(p.spindle_stop_code)
         self.gcode += self.doformat(p.lift_code, x=current_pt[0], y=current_pt[1])
         self.gcode += self.doformat(p.lift_code, x=current_pt[0], y=current_pt[1])
         self.gcode += self.doformat(p.end_code, x=0, y=0)
         self.gcode += self.doformat(p.end_code, x=0, y=0)
-        self.app.inform.emit('%s... %s %s.' %
-                             (_("Finished G-Code generation"),
-                              str(path_count),
-                              _("paths traced")
-                              )
-                             )
+        self.app.inform.emit(
+            '%s... %s %s.' % (_("Finished G-Code generation"), str(path_count), _("paths traced"))
+        )
         return self.gcode
         return self.gcode
 
 
     def generate_from_geometry_2(
     def generate_from_geometry_2(
@@ -3418,8 +3415,7 @@ class CNCjob(Geometry):
         """
         """
 
 
         if not isinstance(geometry, Geometry):
         if not isinstance(geometry, Geometry):
-            self.app.inform.emit('[ERROR] %s: %s' %
-                                 (_("Expected a Geometry, got"), type(geometry)))
+            self.app.inform.emit('[ERROR] %s: %s' % (_("Expected a Geometry, got"), type(geometry)))
             return 'fail'
             return 'fail'
         log.debug("Executing camlib.CNCJob.generate_from_geometry_2()")
         log.debug("Executing camlib.CNCJob.generate_from_geometry_2()")
 
 
@@ -3465,10 +3461,11 @@ class CNCjob(Geometry):
                 # if the offset is less than half of the total length or less than half of the total width of the
                 # if the offset is less than half of the total length or less than half of the total width of the
                 # solid geometry it's obvious we can't do the offset
                 # solid geometry it's obvious we can't do the offset
                 if -offset > ((c - a) / 2) or -offset > ((d - b) / 2):
                 if -offset > ((c - a) / 2) or -offset > ((d - b) / 2):
-                    self.app.inform.emit('[ERROR_NOTCL] %s' % _(
-                        "The Tool Offset value is too negative to use "
-                        "for the current_geometry.\n"
-                        "Raise the value (in module) and try again."))
+                    self.app.inform.emit(
+                        '[ERROR_NOTCL] %s' %
+                        _("The Tool Offset value is too negative to use for the current_geometry.\n"
+                          "Raise the value (in module) and try again.")
+                    )
                     return 'fail'
                     return 'fail'
                 # hack: make offset smaller by 0.0000000001 which is insignificant difference but allow the job
                 # hack: make offset smaller by 0.0000000001 which is insignificant difference but allow the job
                 # to continue
                 # to continue
@@ -3532,9 +3529,11 @@ class CNCjob(Geometry):
             else:
             else:
                 self.xy_toolchange = [float(eval(a)) for a in toolchangexy.split(",")]
                 self.xy_toolchange = [float(eval(a)) for a in toolchangexy.split(",")]
                 if len(self.xy_toolchange) < 2:
                 if len(self.xy_toolchange) < 2:
-                    self.app.inform.emit('[ERROR] %s' %
-                                         _("The Toolchange X,Y field in Edit -> Preferences has to be "
-                                           "in the format (x, y) \nbut now there is only one value, not two. "))
+                    self.app.inform.emit(
+                        '[ERROR] %s' %
+                        _("The Toolchange X,Y field in Edit -> Preferences has to be in the format (x, y) \n"
+                          "but now there is only one value, not two. ")
+                    )
                     return 'fail'
                     return 'fail'
         except Exception as e:
         except Exception as e:
             log.debug("camlib.CNCJob.generate_from_geometry_2() --> %s" % str(e))
             log.debug("camlib.CNCJob.generate_from_geometry_2() --> %s" % str(e))
@@ -3545,9 +3544,10 @@ class CNCjob(Geometry):
 
 
         if self.machinist_setting == 0:
         if self.machinist_setting == 0:
             if self.z_cut is None:
             if self.z_cut is None:
-                self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                     _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
-                                       "other parameters."))
+                self.app.inform.emit(
+                    '[ERROR_NOTCL] %s' % _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
+                                           "other parameters.")
+                )
                 return 'fail'
                 return 'fail'
 
 
             if self.z_cut > 0:
             if self.z_cut > 0:
@@ -3559,14 +3559,14 @@ class CNCjob(Geometry):
                                        "Check the resulting CNC code (Gcode etc)."))
                                        "Check the resulting CNC code (Gcode etc)."))
                 self.z_cut = -self.z_cut
                 self.z_cut = -self.z_cut
             elif self.z_cut == 0:
             elif self.z_cut == 0:
-                self.app.inform.emit('[WARNING] %s: %s' %
-                                     (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
-                                      geometry.options['name']))
+                self.app.inform.emit(
+                    '[WARNING] %s: %s' % (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
+                                          geometry.options['name'])
+                )
                 return 'fail'
                 return 'fail'
 
 
             if self.z_move is None:
             if self.z_move is None:
-                self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                     _("Travel Z parameter is None or zero."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Travel Z parameter is None or zero."))
                 return 'fail'
                 return 'fail'
 
 
             if self.z_move < 0:
             if self.z_move < 0:
@@ -3578,9 +3578,10 @@ class CNCjob(Geometry):
                                        "Check the resulting CNC code (Gcode etc)."))
                                        "Check the resulting CNC code (Gcode etc)."))
                 self.z_move = -self.z_move
                 self.z_move = -self.z_move
             elif self.z_move == 0:
             elif self.z_move == 0:
-                self.app.inform.emit('[WARNING] %s: %s' %
-                                     (_("The Z Travel parameter is zero. "
-                                       "This is dangerous, skipping file"), self.options['name']))
+                self.app.inform.emit(
+                    '[WARNING] %s: %s' % (_("The Z Travel parameter is zero. This is dangerous, skipping file"),
+                                          self.options['name'])
+                )
                 return 'fail'
                 return 'fail'
 
 
         # made sure that depth_per_cut is no more then the z_cut
         # made sure that depth_per_cut is no more then the z_cut
@@ -3663,7 +3664,7 @@ class CNCjob(Geometry):
 
 
         # Iterate over geometry paths getting the nearest each time.
         # Iterate over geometry paths getting the nearest each time.
         log.debug("Starting G-Code...")
         log.debug("Starting G-Code...")
-        self.app.inform.emit(_("Starting G-Code..."))
+        self.app.inform.emit('%s...' % _("Starting G-Code"))
 
 
         # variables to display the percentage of work done
         # variables to display the percentage of work done
         geo_len = len(flat_geometry)
         geo_len = len(flat_geometry)
@@ -3674,9 +3675,7 @@ class CNCjob(Geometry):
         current_tooldia = float('%.*f' % (self.decimals, float(self.tooldia)))
         current_tooldia = float('%.*f' % (self.decimals, float(self.tooldia)))
 
 
         self.app.inform.emit(
         self.app.inform.emit(
-            '%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
-                           str(current_tooldia),
-                           str(self.units))
+            '%s: %s%s.' % (_("Starting G-Code for tool with diameter"), str(current_tooldia), str(self.units))
         )
         )
 
 
         path_count = 0
         path_count = 0
@@ -3859,13 +3858,9 @@ class CNCjob(Geometry):
             pass
             pass
 
 
         log.debug("Finishing SolderPste G-Code... %s paths traced." % path_count)
         log.debug("Finishing SolderPste G-Code... %s paths traced." % path_count)
-        self.app.inform.emit('%s... %s %s' %
-                             (_("Finished SolderPste G-Code generation"),
-                              str(path_count),
-                              _("paths traced.")
-                              )
-                             )
-
+        self.app.inform.emit(
+            '%s... %s %s' % (_("Finished SolderPste G-Code generation"), str(path_count), _("paths traced."))
+        )
 
 
         # Finish
         # Finish
         self.gcode += self.doformat(p.lift_code)
         self.gcode += self.doformat(p.lift_code)
@@ -4064,9 +4059,9 @@ class CNCjob(Geometry):
                 command['X'] = float(match_lsr.group(1).replace(" ", ""))
                 command['X'] = float(match_lsr.group(1).replace(" ", ""))
                 command['Y'] = float(match_lsr.group(2).replace(" ", ""))
                 command['Y'] = float(match_lsr.group(2).replace(" ", ""))
 
 
-            match_lsr_pos = re.search(r"^(M0[3-5])", gline)
+            match_lsr_pos = re.search(r"^(M0?[3-5])", gline)
             if match_lsr_pos:
             if match_lsr_pos:
-                if 'M05' in match_lsr_pos.group(1):
+                if 'M05' in match_lsr_pos.group(1) or 'M5' in match_lsr_pos.group(1):
                     # the value does not matter, only that it is positive so the gcode_parse() know it is > 0,
                     # the value does not matter, only that it is positive so the gcode_parse() know it is > 0,
                     # therefore the move is of kind T (travel)
                     # therefore the move is of kind T (travel)
                     command['Z'] = 1
                     command['Z'] = 1

+ 38 - 42
flatcamGUI/ObjectUI.py

@@ -895,8 +895,8 @@ class ExcellonObjectUI(ObjectUI):
         self.grid3.addWidget(self.mill_dia_entry, 3, 1)
         self.grid3.addWidget(self.mill_dia_entry, 3, 1)
 
 
         # Cut Z
         # Cut Z
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        cutzlabel.setToolTip(
+        self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        self.cutzlabel.setToolTip(
             _("Drill depth (negative)\n"
             _("Drill depth (negative)\n"
               "below the copper surface.")
               "below the copper surface.")
         )
         )
@@ -911,7 +911,7 @@ class ExcellonObjectUI(ObjectUI):
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(cutzlabel, 4, 0)
+        self.grid3.addWidget(self.cutzlabel, 4, 0)
         self.grid3.addWidget(self.cutz_entry, 4, 1)
         self.grid3.addWidget(self.cutz_entry, 4, 1)
 
 
         # Multi-Depth
         # Multi-Depth
@@ -930,19 +930,15 @@ class ExcellonObjectUI(ObjectUI):
         self.maxdepth_entry.set_range(0, 9999.9999)
         self.maxdepth_entry.set_range(0, 9999.9999)
         self.maxdepth_entry.setSingleStep(0.1)
         self.maxdepth_entry.setSingleStep(0.1)
 
 
-        self.maxdepth_entry.setToolTip(
-            _(
-                "Depth of each pass (positive)."
-            )
-        )
+        self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
         self.mis_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
         self.mis_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
 
 
         self.grid3.addWidget(self.mpass_cb, 5, 0)
         self.grid3.addWidget(self.mpass_cb, 5, 0)
         self.grid3.addWidget(self.maxdepth_entry, 5, 1)
         self.grid3.addWidget(self.maxdepth_entry, 5, 1)
 
 
         # Travel Z (z_move)
         # Travel Z (z_move)
-        travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
-        travelzlabel.setToolTip(
+        self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
+        self.travelzlabel.setToolTip(
             _("Tool height when travelling\n"
             _("Tool height when travelling\n"
               "across the XY plane.")
               "across the XY plane.")
         )
         )
@@ -957,7 +953,7 @@ class ExcellonObjectUI(ObjectUI):
 
 
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(travelzlabel, 6, 0)
+        self.grid3.addWidget(self.travelzlabel, 6, 0)
         self.grid3.addWidget(self.travelz_entry, 6, 1)
         self.grid3.addWidget(self.travelz_entry, 6, 1)
 
 
         # Tool change:
         # Tool change:
@@ -1029,20 +1025,20 @@ class ExcellonObjectUI(ObjectUI):
         self.grid3.addWidget(self.xyfeedrate_entry, 12, 1)
         self.grid3.addWidget(self.xyfeedrate_entry, 12, 1)
 
 
         # Excellon Feedrate Z
         # Excellon Feedrate Z
-        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
-        frlabel.setToolTip(
+        self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
+        self.frzlabel.setToolTip(
             _("Tool speed while drilling\n"
             _("Tool speed while drilling\n"
               "(in units per minute).\n"
               "(in units per minute).\n"
               "So called 'Plunge' feedrate.\n"
               "So called 'Plunge' feedrate.\n"
               "This is for linear move G01.")
               "This is for linear move G01.")
         )
         )
-        self.feedrate_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.feedrate_entry.set_precision(self.decimals)
-        self.feedrate_entry.set_range(0.0, 99999.9999)
-        self.feedrate_entry.setSingleStep(0.1)
+        self.feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.feedrate_z_entry.set_precision(self.decimals)
+        self.feedrate_z_entry.set_range(0.0, 99999.9999)
+        self.feedrate_z_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(frlabel, 14, 0)
-        self.grid3.addWidget(self.feedrate_entry, 14, 1)
+        self.grid3.addWidget(self.frzlabel, 14, 0)
+        self.grid3.addWidget(self.feedrate_z_entry, 14, 1)
 
 
         # Excellon Rapid Feedrate
         # Excellon Rapid Feedrate
         self.feedrate_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
         self.feedrate_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
@@ -1092,8 +1088,8 @@ class ExcellonObjectUI(ObjectUI):
         self.grid3.addWidget(self.e_cut_entry, 17, 1)
         self.grid3.addWidget(self.e_cut_entry, 17, 1)
 
 
         # Spindlespeed
         # Spindlespeed
-        spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
-        spdlabel.setToolTip(
+        self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
+        self.spindle_label.setToolTip(
             _("Speed of the spindle\n"
             _("Speed of the spindle\n"
               "in RPM (optional)")
               "in RPM (optional)")
         )
         )
@@ -1102,7 +1098,7 @@ class ExcellonObjectUI(ObjectUI):
         self.spindlespeed_entry.set_range(0, 1000000)
         self.spindlespeed_entry.set_range(0, 1000000)
         self.spindlespeed_entry.setSingleStep(100)
         self.spindlespeed_entry.setSingleStep(100)
 
 
-        self.grid3.addWidget(spdlabel, 19, 0)
+        self.grid3.addWidget(self.spindle_label, 19, 0)
         self.grid3.addWidget(self.spindlespeed_entry, 19, 1)
         self.grid3.addWidget(self.spindlespeed_entry, 19, 1)
 
 
         # Dwell
         # Dwell
@@ -1647,8 +1643,8 @@ class GeometryObjectUI(ObjectUI):
         self.grid3.addWidget(self.tipangle_entry, 2, 1)
         self.grid3.addWidget(self.tipangle_entry, 2, 1)
 
 
         # Cut Z
         # Cut Z
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        cutzlabel.setToolTip(
+        self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        self.cutzlabel.setToolTip(
             _(
             _(
                 "Cutting depth (negative)\n"
                 "Cutting depth (negative)\n"
                 "below the copper surface."
                 "below the copper surface."
@@ -1664,7 +1660,7 @@ class GeometryObjectUI(ObjectUI):
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(cutzlabel, 3, 0)
+        self.grid3.addWidget(self.cutzlabel, 3, 0)
         self.grid3.addWidget(self.cutz_entry, 3, 1)
         self.grid3.addWidget(self.cutz_entry, 3, 1)
 
 
         # Multi-pass
         # Multi-pass
@@ -1694,8 +1690,8 @@ class GeometryObjectUI(ObjectUI):
         self.grid3.addWidget(self.maxdepth_entry, 4, 1)
         self.grid3.addWidget(self.maxdepth_entry, 4, 1)
 
 
         # Travel Z
         # Travel Z
-        travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
-        travelzlabel.setToolTip(
+        self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
+        self.travelzlabel.setToolTip(
             _("Height of the tool when\n"
             _("Height of the tool when\n"
               "moving without cutting.")
               "moving without cutting.")
         )
         )
@@ -1709,7 +1705,7 @@ class GeometryObjectUI(ObjectUI):
 
 
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(travelzlabel, 5, 0)
+        self.grid3.addWidget(self.travelzlabel, 5, 0)
         self.grid3.addWidget(self.travelz_entry, 5, 1)
         self.grid3.addWidget(self.travelz_entry, 5, 1)
 
 
         # Tool change
         # Tool change
@@ -1771,8 +1767,8 @@ class GeometryObjectUI(ObjectUI):
         self.grid3.addWidget(self.gendz_entry, 9, 1)
         self.grid3.addWidget(self.gendz_entry, 9, 1)
 
 
         # Feedrate X-Y
         # Feedrate X-Y
-        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y'))
-        frlabel.setToolTip(
+        self.frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y'))
+        self.frlabel.setToolTip(
             _("Cutting speed in the XY\n"
             _("Cutting speed in the XY\n"
               "plane in units per minute")
               "plane in units per minute")
         )
         )
@@ -1781,23 +1777,23 @@ class GeometryObjectUI(ObjectUI):
         self.cncfeedrate_entry.set_range(0, 99999.9999)
         self.cncfeedrate_entry.set_range(0, 99999.9999)
         self.cncfeedrate_entry.setSingleStep(0.1)
         self.cncfeedrate_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(frlabel, 10, 0)
+        self.grid3.addWidget(self.frlabel, 10, 0)
         self.grid3.addWidget(self.cncfeedrate_entry, 10, 1)
         self.grid3.addWidget(self.cncfeedrate_entry, 10, 1)
 
 
         # Feedrate Z (Plunge)
         # Feedrate Z (Plunge)
-        frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
-        frzlabel.setToolTip(
+        self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
+        self.frzlabel.setToolTip(
             _("Cutting speed in the XY\n"
             _("Cutting speed in the XY\n"
               "plane in units per minute.\n"
               "plane in units per minute.\n"
               "It is called also Plunge.")
               "It is called also Plunge.")
         )
         )
-        self.cncplunge_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.cncplunge_entry.set_precision(self.decimals)
-        self.cncplunge_entry.set_range(0, 99999.9999)
-        self.cncplunge_entry.setSingleStep(0.1)
+        self.feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.feedrate_z_entry.set_precision(self.decimals)
+        self.feedrate_z_entry.set_range(0, 99999.9999)
+        self.feedrate_z_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(frzlabel, 11, 0)
-        self.grid3.addWidget(self.cncplunge_entry, 11, 1)
+        self.grid3.addWidget(self.frzlabel, 11, 0)
+        self.grid3.addWidget(self.feedrate_z_entry, 11, 1)
 
 
         # Feedrate rapids
         # Feedrate rapids
         self.fr_rapidlabel = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
         self.fr_rapidlabel = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
@@ -1843,8 +1839,8 @@ class GeometryObjectUI(ObjectUI):
         self.grid3.addWidget(self.e_cut_entry, 13, 1)
         self.grid3.addWidget(self.e_cut_entry, 13, 1)
 
 
         # Spindlespeed
         # Spindlespeed
-        spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
-        spdlabel.setToolTip(
+        self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
+        self.spindle_label.setToolTip(
             _(
             _(
                 "Speed of the spindle in RPM (optional).\n"
                 "Speed of the spindle in RPM (optional).\n"
                 "If LASER preprocessor is used,\n"
                 "If LASER preprocessor is used,\n"
@@ -1855,7 +1851,7 @@ class GeometryObjectUI(ObjectUI):
         self.cncspindlespeed_entry.set_range(0, 1000000)
         self.cncspindlespeed_entry.set_range(0, 1000000)
         self.cncspindlespeed_entry.setSingleStep(100)
         self.cncspindlespeed_entry.setSingleStep(100)
 
 
-        self.grid3.addWidget(spdlabel, 14, 0)
+        self.grid3.addWidget(self.spindle_label, 14, 0)
         self.grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
         self.grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
 
 
         # Dwell
         # Dwell

+ 10 - 10
flatcamGUI/PreferencesUI.py

@@ -3152,12 +3152,12 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
               "So called 'Plunge' feedrate.\n"
               "So called 'Plunge' feedrate.\n"
               "This is for linear move G01.")
               "This is for linear move G01.")
         )
         )
-        self.feedrate_entry = FCDoubleSpinner()
-        self.feedrate_entry.set_precision(self.decimals)
-        self.feedrate_entry.set_range(0, 99999.9999)
+        self.feedrate_z_entry = FCDoubleSpinner()
+        self.feedrate_z_entry.set_precision(self.decimals)
+        self.feedrate_z_entry.set_range(0, 99999.9999)
 
 
         grid2.addWidget(frlabel, 5, 0)
         grid2.addWidget(frlabel, 5, 0)
-        grid2.addWidget(self.feedrate_entry, 5, 1)
+        grid2.addWidget(self.feedrate_z_entry, 5, 1)
 
 
         # Spindle speed
         # Spindle speed
         spdlabel = QtWidgets.QLabel('%s:' % _('Spindle Speed'))
         spdlabel = QtWidgets.QLabel('%s:' % _('Spindle Speed'))
@@ -4096,14 +4096,14 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
               "plane in units per minute.\n"
               "plane in units per minute.\n"
               "It is called also Plunge.")
               "It is called also Plunge.")
         )
         )
-        self.cncplunge_entry = FCDoubleSpinner()
-        self.cncplunge_entry.set_range(0, 99999.9999)
-        self.cncplunge_entry.set_precision(self.decimals)
-        self.cncplunge_entry.setSingleStep(0.1)
-        self.cncplunge_entry.setWrapping(True)
+        self.feedrate_z_entry = FCDoubleSpinner()
+        self.feedrate_z_entry.set_range(0, 99999.9999)
+        self.feedrate_z_entry.set_precision(self.decimals)
+        self.feedrate_z_entry.setSingleStep(0.1)
+        self.feedrate_z_entry.setWrapping(True)
 
 
         grid1.addWidget(frz_label, 8, 0)
         grid1.addWidget(frz_label, 8, 0)
-        grid1.addWidget(self.cncplunge_entry, 8, 1)
+        grid1.addWidget(self.feedrate_z_entry, 8, 1)
 
 
         # Spindle Speed
         # Spindle Speed
         spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
         spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))

+ 2 - 2
preprocessors/marlin.py → preprocessors/Marlin.py

@@ -9,7 +9,7 @@
 from FlatCAMPostProc import *
 from FlatCAMPostProc import *
 
 
 
 
-class marlin(FlatCAMPostProc):
+class Marlin(FlatCAMPostProc):
 
 
     include_header = True
     include_header = True
     coordinate_format = "%.*f"
     coordinate_format = "%.*f"
@@ -34,7 +34,7 @@ class marlin(FlatCAMPostProc):
         if str(p['options']['type']) == 'Geometry':
         if str(p['options']['type']) == 'Geometry':
             gcode += ';Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + '\n'
             gcode += ';Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + '\n'
 
 
-        gcode += ';Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
+        gcode += ';Feedrate rapids: ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
         gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n'
         gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n'
 
 
         if str(p['options']['type']) == 'Geometry':
         if str(p['options']['type']) == 'Geometry':

+ 122 - 0
preprocessors/Marlin_laser.py

@@ -0,0 +1,122 @@
+# ##########################################################
+# FlatCAM: 2D Post-processing for Manufacturing            #
+# Website:      http://flatcam.org                         #
+# File Author:  Marius Adrian Stanciu (c)                  #
+# Date:         8-Feb-2020                                 #
+# License:      MIT Licence                                #
+# ##########################################################
+
+from FlatCAMPostProc import *
+
+
+class Marlin_laser(FlatCAMPostProc):
+
+    include_header = True
+    coordinate_format = "%.*f"
+    feedrate_format = '%.*f'
+    feedrate_rapid_format = feedrate_format
+
+    def start_code(self, p):
+        units = ' ' + str(p['units']).lower()
+        coords_xy = p['xy_toolchange']
+        gcode = ''
+
+        xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
+        xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
+        ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
+        ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
+
+        gcode += ';Feedrate: ' + str(p['feedrate']) + units + '/min' + '\n'
+        gcode += ';Feedrate rapids: ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
+
+        gcode += ';Z Focus: ' + str(p['z_move']) + 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 += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n'
+        else:
+            gcode += ';Preprocessor Geometry: ' + str(p['pp_geometry_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 += ';Laser Power (Spindle Speed): ' + str(p['spindlespeed']) + '\n' + '\n'
+
+        gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
+        gcode += 'G90\n'
+        gcode += 'G94'
+
+        return gcode
+
+    def startz_code(self, p):
+        if p.startz is not None:
+            return 'G0 Z' + self.coordinate_format % (p.coords_decimals, p.z_move)
+        else:
+            return ''
+
+    def lift_code(self, p):
+        gcode = 'M400\n'
+        gcode += 'M5 S0'
+        return gcode
+
+    def down_code(self, p):
+        sdir = {'CW': 'M3', 'CCW': 'M4'}[p.spindledir]
+        if p.spindlespeed:
+            return '%s S%s' % (sdir, str(p.spindlespeed))
+        else:
+            return sdir
+
+    def toolchange_code(self, p):
+        return ''
+
+    def up_to_zero_code(self, p):
+        gcode = 'M400\n'
+        gcode += 'M5'
+        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 ('G0 ' + self.position_code(p)).format(**p) + " " + self.feedrate_rapid_code(p)
+
+    def linear_code(self, p):
+        return ('G1 ' + self.position_code(p)).format(**p) + " " + self.inline_feedrate_code(p)
+
+    def end_code(self, p):
+        coords_xy = p['xy_toolchange']
+        gcode = ('G0 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + " " + self.feedrate_rapid_code(p) + "\n")
+
+        if coords_xy is not None:
+            gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n"
+
+        return gcode
+
+    def feedrate_code(self, p):
+        return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, p.feedrate))
+
+    def z_feedrate_code(self, p):
+        return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, p.z_feedrate))
+
+    def inline_feedrate_code(self, p):
+        return 'F' + self.feedrate_format % (p.fr_decimals, p.feedrate)
+
+    def feedrate_rapid_code(self, p):
+        return 'F' + self.feedrate_rapid_format % (p.fr_decimals, p.feedrate_rapid)
+
+    def spindle_code(self, p):
+        sdir = {'CW': 'M3', 'CCW': 'M4'}[p.spindledir]
+        if p.spindlespeed:
+            return '%s S%s' % (sdir, str(p.spindlespeed))
+        else:
+            return sdir
+
+    def dwell_code(self, p):
+        return ''
+
+    def spindle_stop_code(self, p):
+        gcode = 'M400\n'
+        gcode += 'M5'
+        return gcode

+ 6 - 2
preprocessors/grbl_laser.py

@@ -28,7 +28,9 @@ class grbl_laser(FlatCAMPostProc):
         ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
         ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
 
 
         gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
         gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
-        gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
+        gcode += '(Feedrate rapids: ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
+
+        gcode += '(Z Focus: ' + str(p['z_move']) + units + ')\n'
 
 
         gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
         gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
 
 
@@ -40,10 +42,12 @@ class grbl_laser(FlatCAMPostProc):
         gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\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 += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
 
 
+        gcode += '(Laser Power (Spindle Speed): ' + str(p['spindlespeed']) + ')\n\n'
+
         gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
         gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
         gcode += 'G90\n'
         gcode += 'G90\n'
         gcode += 'G17\n'
         gcode += 'G17\n'
-        gcode += 'G94\n'
+        gcode += 'G94'
 
 
         return gcode
         return gcode