GRBL new post processor with drill cycles G81..G83

Here's the place for discussion related to CAM/CNC and the development of the Path module.
User avatar
freman
Posts: 1280
Joined: Tue Nov 27, 2018 10:30 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby freman » Sat Oct 26, 2019 6:28 pm

Yes, a post processor preferences and options panel is a good idea.

I've solved the situation for my case by modifying the source since I'm building it. But a more general solution would be good.

@+.
User avatar
freman
Posts: 1280
Joined: Tue Nov 27, 2018 10:30 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby freman » Sat Oct 26, 2019 7:04 pm

mlampert wrote:
Sat Oct 26, 2019 6:11 pm
Out of curiosity I checked the history and it seems those preferences had their 3rd anniversary 3 days ago - they were added 2016-10-23 :D
I'm not entirely sure what you are meaning by that comment but it sounds like you are saying what Gautier suggested is already present.

He was suggesting an options panel ( presumably a kind of checkbox thing for the binary choice options ) . At the moment, all there is an edit box where you can type in kind of command line option. You need to continually configure this every time you create a job which is quite a drag.

I even find continually needing to select my post processor on every single job a drag. There is little chance I'm flipping constantly from one machine to another needing different PPs each time. Pre-filling the choice with the last used PP would be a nice trivial feature mod that would make the work flow easier. If any typed options could also be made persistent that would get around some of the need for a panel ( though not having to dig around to find the exact text required would still be a plus for a gui approach ).
mlampert
Posts: 1491
Joined: Fri Sep 16, 2016 9:28 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby mlampert » Sat Oct 26, 2019 9:58 pm

freman wrote:
Sat Oct 26, 2019 7:04 pm
mlampert wrote:
Sat Oct 26, 2019 6:11 pm
Out of curiosity I checked the history and it seems those preferences had their 3rd anniversary 3 days ago - they were added 2016-10-23 :D
I'm not entirely sure what you are meaning by that comment but it sounds like you are saying what Gautier suggested is already present.

He was suggesting an options panel ( presumably a kind of checkbox thing for the binary choice options ) . At the moment, all there is an edit box where you can type in kind of command line option. You need to continually configure this every time you create a job which is quite a drag.

I even find continually needing to select my post processor on every single job a drag. There is little chance I'm flipping constantly from one machine to another needing different PPs each time. Pre-filling the choice with the last used PP would be a nice trivial feature mod that would make the work flow easier. If any typed options could also be made persistent that would get around some of the need for a panel ( though not having to dig around to find the exact text required would still be a plus for a gui approach ).
If you would have cared to open up the preferences before getting on your soap box you might have noticed the path output section. But I guess that's too much to ask for these days. Also, I have pointed at the job templates several times before which can also specify the post processor - which can also be set in the preferences. Can't help ya if ya ain't using what's there.
chrisb
Posts: 29100
Joined: Tue Mar 17, 2015 9:14 am

Re: GRBL new post processor with drill cycles G81..G83

Postby chrisb » Sat Oct 26, 2019 10:23 pm

I would second the usage of templates. Invest these 13:25 to watch sliptonic's video or start at least at 5:15 where the templates start. Well prepared templates with tools, filenames, postprocessors including parameters, will save you, let's say, save 1 minute per job. That means that you are on the gaining side after 10 jobs only, That's a fantastic return on investment. And you will make less mistakes.
A Sketcher Lecture with in-depth information is available in English, auf deutsch, en français, en español.
User avatar
freman
Posts: 1280
Joined: Tue Nov 27, 2018 10:30 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby freman » Sun Oct 27, 2019 3:26 am

Thanks Chris, that video is a good guide to where to find all this stuff. I will definitely look into setting up templates for toolcontrollers, that is another thing I'm constantly doing repetitively which wastes a lot of time.
User avatar
freman
Posts: 1280
Joined: Tue Nov 27, 2018 10:30 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby freman » Sun Oct 27, 2019 3:47 am

mlampert wrote:
Sat Oct 26, 2019 9:58 pm

If you would have cared to open up the preferences before getting on your soap box you might have noticed the path output section. But I guess that's too much to ask for these days. Also, I have pointed at the job templates several times before which can also specify the post processor - which can also be set in the preferences. Can't help ya if ya ain't using what's there.
I was not "getting on my soapbox" I was trying to make a useful suggestion where I thought improvements could be made. I did look at prefs but due to screen size ( I only have 1780x 1240 available ;) ) and poor layout I only see General and Geometry with two thirds of the tab window empty below it. It seems pretty clear that this is all there is on that page.


Having watched Siptonic's video , I realise there should be other content and for some reason the other two tabs are "displayed" out of sight below the 2/3 of empty grey space !!

I guess that this is not the way it is designed and may display better or other platforms or different versions of qt.

If users are not finding this stuff it may be more productive to ask why not rather than shouting a people. It may be that the interface which has evolved piecemeal over time is not optimal, or that modified code is never getting tested properly before getting merged.

The recent breakage of the toolcontroller code which means that default build of FC no longer work at all for Path, is one example which comes to mind.
User avatar
freman
Posts: 1280
Joined: Tue Nov 27, 2018 10:30 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby freman » Sun Oct 27, 2019 3:57 am

Having used the preference page for Path to set a few options I find this in report view. ( Options do seem to have stuck AFAICT ).

Code: Select all

Traceback (most recent call last):
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 262, in setProcessorListTooltip
    self.setPostProcessorTooltip(self.form.postProcessorList, item.text(), '')
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 255, in setPostProcessorTooltip
    processor = self.getPostProcessor(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 249, in getPostProcessor
    processor = PostProcessor.load(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPostProcessor.py", line 49, in load
    exec("import %s as current_post" % postname, namespace) # pylint: disable=exec-used
  File "<string>", line 1, in <module>
  File "~/freecad-master-build/Mod/Path/PathScripts//post/centroid_post.py", line 82, in <module>
    '''.format(__name__, FreeCAD.ActiveDocument.FileName, str(now))
AttributeError: 'NoneType' object has no attribute 'FileName'
Traceback (most recent call last):
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 262, in setProcessorListTooltip
    self.setPostProcessorTooltip(self.form.postProcessorList, item.text(), '')
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 255, in setPostProcessorTooltip
    processor = self.getPostProcessor(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 249, in getPostProcessor
    processor = PostProcessor.load(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPostProcessor.py", line 49, in load
    exec("import %s as current_post" % postname, namespace) # pylint: disable=exec-used
  File "<string>", line 1, in <module>
  File "~/freecad-master-build/Mod/Path/PathScripts//post/centroid_post.py", line 82, in <module>
    '''.format(__name__, FreeCAD.ActiveDocument.FileName, str(now))
AttributeError: 'NoneType' object has no attribute 'FileName'
Traceback (most recent call last):
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 262, in setProcessorListTooltip
    self.setPostProcessorTooltip(self.form.postProcessorList, item.text(), '')
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 255, in setPostProcessorTooltip
    processor = self.getPostProcessor(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 249, in getPostProcessor
    processor = PostProcessor.load(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPostProcessor.py", line 49, in load
    exec("import %s as current_post" % postname, namespace) # pylint: disable=exec-used
  File "<string>", line 1, in <module>
  File "~/freecad-master-build/Mod/Path/PathScripts//post/centroid_post.py", line 82, in <module>
    '''.format(__name__, FreeCAD.ActiveDocument.FileName, str(now))
AttributeError: 'NoneType' object has no attribute 'FileName'
Traceback (most recent call last):
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 262, in setProcessorListTooltip
    self.setPostProcessorTooltip(self.form.postProcessorList, item.text(), '')
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 255, in setPostProcessorTooltip
    processor = self.getPostProcessor(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 249, in getPostProcessor
    processor = PostProcessor.load(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPostProcessor.py", line 49, in load
    exec("import %s as current_post" % postname, namespace) # pylint: disable=exec-used
  File "<string>", line 1, in <module>
  File "~/freecad-master-build/Mod/Path/PathScripts//post/centroid_post.py", line 82, in <module>
    '''.format(__name__, FreeCAD.ActiveDocument.FileName, str(now))
AttributeError: 'NoneType' object has no attribute 'FileName'
Traceback (most recent call last):
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 262, in setProcessorListTooltip
    self.setPostProcessorTooltip(self.form.postProcessorList, item.text(), '')
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 255, in setPostProcessorTooltip
    processor = self.getPostProcessor(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPreferencesPathJob.py", line 249, in getPostProcessor
    processor = PostProcessor.load(name)
  File "~/freecad-master-build/Mod/Path/PathScripts/PathPostProcessor.py", line 49, in load
    exec("import %s as current_post" % postname, namespace) # pylint: disable=exec-used
  File "<string>", line 1, in <module>
  File "~/freecad-master-build/Mod/Path/PathScripts//post/centroid_post.py", line 82, in <module>
    '''.format(__name__, FreeCAD.ActiveDocument.FileName, str(now))
AttributeError: 'NoneType' object has no attribute 'FileName'

OS: Linux (LXDE/LXDE)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.17672 (Git)
Build type: Unknown
Branch: (HEAD detached at ffb4aa5dc)
Hash: ffb4aa5dcfc0f0b6fb37161fd1c820ade167eb04
Python version: 2.7.16
Qt version: 4.8.7
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: English/UnitedKingdom (en_GB)
mlampert
Posts: 1491
Joined: Fri Sep 16, 2016 9:28 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby mlampert » Sun Oct 27, 2019 5:23 am

My bad, I thought this thread was about grbl.
smerrett79
Posts: 1
Joined: Thu Dec 19, 2019 4:54 pm

Re: GRBL new post processor with drill cycles G81..G83

Postby smerrett79 » Mon Jan 06, 2020 2:08 pm

Hi, first post so please go easy. I have been very pleased with my transition from Fusion360 to FreeCAD on the CAM front - really well done to all the contributors and thank you.

I'm trying to use a relatively flexible (as opposed to rigid) DIY CNC router controlled with grbl and ugcs to make holes in some 15mm and 20mm alu plates as part of a "reprap" style upgrade to the frame. I have profile cut alu with a 3mm 2 fluted endmill before but I didn't do enough to dial in feeds/speeds, just enough to say my machine could do it!

The upgrade involves cutting a couple of hundred 5mm through holes in several plates, for the rails, linear bearing carriages, ball nut traps and bearing blocks etc. I knew that my 3mm endmill would not be a good choice for pocket/profile or helix cutting these, especially as when I cut out my proof parts (using Fusion, over a year ago) the scariest noises happened during the (slow) plunging.

So I tried a 5mm stub drill with manual gcode sent through the ugcs console and the noise/vibration was horrible. Chips were like fine dust, even at slower speeds (I promise I was doing some proper calcs but I don't think the "500W" spindle I have is going to live very long at high torque low rpm drilling operations). Then I went and found some people had success reducing vibration using a 3 flute endmill for alu drilling operations with smaller machines. I received the 3 flute 5mm endmill with a 45 degree flute today and tried it with some manual gcode and the sound was really quite acceptable, vibration was low and the chips were like coarse sand. So far so good.

So I would like to generate gcode from Path (which I have successfully done with profile and helix operations already during test cuts on MDF with the 3mm endmill) using a canned drilling cycle for 60 holes in one of my plates. I realise I need the grbl_g81 postprocessor to do this but the output has two elements that I wasn't expecting.

Firstly, I have set a single hole, pecking 0.3mm, retracting 0.1mm and dwelling 1.0 (second?). This is the resultant gcode from the normal grbl_post:

Code: Select all

(Exported by FreeCAD)
(Post Processor: grbl_post)
(Output Time:2020-01-06 12:13:27.479520)
(begin preamble)
G17 G90
G21
(begin operation: T1: 5mm_3flute_endmill_45degree)
(Path: T1: 5mm_3flute_endmill_45degree)
(T1: 5mm_3flute_endmill_45degree)
(begin toolchange)
; M6 T2
M3 S1500.0000
(finish operation: T1: 5mm_3flute_endmill_45degree)
(begin operation: Drilling)
(Path: Drilling)
(Drilling)
(Begin Drilling)
G0 Z19.0000
G90
; G98
G83 X7.5000 Y6.5000 Z0.0000 F6.00 Q0.3000 R0.1000
; G80
G0 Z19.0000
(finish operation: Drilling)
(begin postamble)
M5
G17 G90
; M2
But I appear to lose the dwell and retract in grbl_81_post:

Code: Select all

(Exported by FreeCAD)
(Post Processor: grbl_G81_post)
(Output Time:2020-01-06 12:41:29.001765)
(begin preamble)
G17 G90
G21
(begin operation: T1: 5mm_3flute_endmill_45degree)
(Path: T1: 5mm_3flute_endmill_45degree)
(T1: 5mm_3flute_endmill_45degree)
(begin toolchange)
(M6 T2.0)
M3 S1500.000
(finish operation: T1: 5mm_3flute_endmill_45degree)
(begin operation: Drilling)
(Path: Drilling)
(Drilling)
(Begin Drilling)
G0 Z19.000
G90
(G98)
(G83 X7.500 Y6.500 Z0.000 F6.00 Q0.300 R0.100)
G0 X7.500 Y6.500
G1 Z18.700 F6.00
G0 Z19.000
G1 Z18.400 F6.00
G0 Z19.000
G1 Z18.100 F6.00
G0 Z19.000
G1 Z17.800 F6.00
G0 Z19.000
G1 Z17.500 F6.00
G0 Z19.000
G1 Z17.200 F6.00
G0 Z19.000
G1 Z16.900 F6.00
G0 Z19.000
G1 Z16.600 F6.00
G0 Z19.000
G1 Z16.300 F6.00
G0 Z19.000
G1 Z16.000 F6.00
G0 Z19.000
G1 Z15.700 F6.00
...
There's also the matter of me not having set the start height right (Z = 19mm, which is the clearance height, rather than 15mm which is the stock surface) but I will find that out without bothering you now.

I understand it's sensible to post FreeCAD version details so mine are:
OS: Windows 10
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.18.4 (GitTag)
Build type: Release
Branch: releases/FreeCAD-0-18
Hash: 980bf9060e28555fecd9e3462f68ca74007b70f8
Python version: 3.6.6
Qt version: 5.6.2
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: English/UnitedKingdom (en_GB)



Thanks for any help you can give.

EDIT: so I have managed to see that G98 means the retract will go to the original Z height at which drilling commenced. When I change to G99 (that took me a while to find), grbl_81 post processor results in:

Code: Select all

G17 G90
G21
(begin operation: T1: 5mm_3flute_endmill_45degree)
(Path: T1: 5mm_3flute_endmill_45degree)
(T1: 5mm_3flute_endmill_45degree)
(begin toolchange)
(M6 T2.0)
M3 S1500.000
(finish operation: T1: 5mm_3flute_endmill_45degree)
(begin operation: Drilling)
(Path: Drilling)
(Drilling)
(Begin Drilling)
G0 Z19.000
G90
G99
(G83 X7.500 Y6.500 Z0.000 F6.00 Q0.300 R0.100)
G0 X7.500 Y6.500
G0 Z19.000
G1 Z0.000 F6.00
G0 Z0.100
(G80)
G0 Z19.000
(finish operation: Drilling)
(begin postamble)
M5
G17 G90
M2
Which isn't what I expected or wanted. I think I will have a look at the grbl_81_post.py and see if I can make the necessary changes there.

ANOTHER UPDATE: I have managed to get the output that I wanted. I had to change the code in grbl_81_post.py (which I named grbl_83_post.py and here's the pecking with a retract that suits what I understand G99 to be

Code: Select all

(Exported by FreeCAD)
(Post Processor: grbl_G83_post)
(Output Time:2020-01-07 00:38:33.770124)
(begin preamble)
G17 G90
G21
(begin operation: T1: 5mm_3flute_endmill_45degree)
(Path: T1: 5mm_3flute_endmill_45degree)
(T1: 5mm_3flute_endmill_45degree)
(begin toolchange)
(M6 T2.0)
M3 S1500.000
(finish operation: T1: 5mm_3flute_endmill_45degree)
(begin operation: Drilling)
(Path: Drilling)
(Drilling)
(Begin Drilling)
G0 Z19.000
G90
(G99)
(G83 X7.500 Y6.500 Z0.000 F6.00 Q0.300 R0.100)
G0 X7.500 Y6.500
G1 Z18.700 F6.00
G0 Z18.800
G1 Z18.400 F6.00
G0 Z18.500
G1 Z18.100 F6.00
G0 Z18.200
G1 Z17.800 F6.00
G0 Z17.900
G1 Z17.500 F6.00
G0 Z17.600
G1 Z17.200 F6.00
G0 Z17.300
I'll have a look on Github to see if I can submit a PR for the changes which now handle G99 and G83 a bit better.

By the way, can anyone tell me how we'd get the drill to rapid to the start drilling height specified in the Path "Start Depth"? At the moment, these post processors seem to be starting the canned cycles (let alone unpacked versions) from the clearance or safe height.

EDIT: the merged version of grbl_post on Github looks significantly different to the version of grbl_81_post that shipped with my version of FreeCAD (details above in this post), so I'll just leave the code here in case anyone wants to incorporate or test for themselves:

Code: Select all

# -*- coding: utf-8 -*-
#***************************************************************************
#*   (c) Gauthier Briere - 2018                                            *
#*   (c) Simon Merrett - 2020                                                                      *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
#*   as published by the Free Software Foundation; either version 2.1 of   *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENSE text file.                                 *
#*                                                                         *
#*   This program is distributed in the hope that it will be useful,       *
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#*   GNU Lesser General Public License for more details.                   *
#*                                                                         *
#*   You should have received a copy of the GNU Library General Public     *
#*   License along with This program; if not, write to the Free Software   *
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#*   USA                                                                   *
#*                                                                         *
#***************************************************************************/


TOOLTIP='''
Generate g-code from a Path that is compatible with the grbl controller.
import grbl_post
grbl_post.export(object, "/path/to/file.ncc")
'''


import FreeCAD
import PathScripts.PostUtils as PostUtils
import argparse
import datetime
import shlex


#*********************************************************************************************************
# Globals set customization preferences
#*********************************************************************************************************

# Default values for command line arguments:
OUTPUT_COMMENTS =        True     # default output of comments in output gCode file
OUTPUT_HEADER =          True     # default output header in output gCode file
OUTPUT_LINE_NUMBERS =    False    # default doesn't output line numbers in output gCode file
SHOW_EDITOR =            True     # default show the resulting file dialog output in GUI
PRECISION =              3        # Default precision for metric (see http://linuxcnc.org/docs/2.7/html/gcode/overview.html#_g_code_best_practices)
TRANSLATE_DRILL_CYCLES = True     # If true, G81, G82 & G83 are translated in G0/G1 moves
PREAMBLE = '''G17 G90
'''                               # default preamble text will appear at the beginning of the gCode output file.
POSTAMBLE = '''M5
G17 G90
M2
'''                               # default postamble text will appear following the last operation.

# Customisation with no command line argument
MODAL =                  False    # if true commands are suppressed if the same as previous line.
LINENR =                   100    # line number starting value
LINEINCR =                  10    # line number increment
OUTPUT_TOOL_CHANGE =     False    # default don't output M6 tool changes (comment it) as grbl currently does not handle it
DRILL_RETRACT_MODE =     'G98'    # Default value of drill retractations (CURRENT_Z) other possible value is G99
MOTION_MODE =            'G90'    # G90 for absolute moves, G91 for relative
UNITS =                  'G21'    # G21 for metric, G20 for us standard
SPEED_MULTIPLIER =          60    # *60 convert FreeCAD's mm/s to mm/mn
PRE_OPERATION =         ''''''    #Pre operation text will be inserted before every operation
POST_OPERATION =        ''''''    #Post operation text will be inserted after every operation
TOOL_CHANGE =           ''''''    #Tool Change commands will be inserted before a tool change

#*********************************************************************************************************
# End of customization
#*********************************************************************************************************

# Parser arguments list & definition
parser = argparse.ArgumentParser(prog='grbl_G81', add_help=False)
parser.add_argument('--comments',           action='store_true', help='output comment (default)')
parser.add_argument('--no-comments',        action='store_true', help='suppress comment output')
parser.add_argument('--header',             action='store_true', help='output headers (default)')
parser.add_argument('--no-header',          action='store_true', help='suppress header output')
parser.add_argument('--line-numbers',       action='store_true', help='prefix with line numbers')
parser.add_argument('--no-line-numbers',    action='store_true', help='don\'t prefix with line numbers (default)')
parser.add_argument('--show-editor',        action='store_true', help='pop up editor before writing output (default)')
parser.add_argument('--no-show-editor',     action='store_true', help='don\'t pop up editor before writing output')
parser.add_argument('--precision',          default='3',         help='number of digits of precision, default=3')
parser.add_argument('--translate_drill',    action='store_true', help='translate drill cycles G81, G82 & G83 in G0/G1 movements (default)')
parser.add_argument('--no-translate_drill', action='store_true', help='don\'t translate drill cycles G81, G82 & G83 in G0/G1 movements')
parser.add_argument('--preamble',                                help='set commands to be issued before the first command, default="G17 G90"')
parser.add_argument('--postamble',                               help='set commands to be issued after the last command, default="M5\nG17 G90\n;M2"')
TOOLTIP_ARGS=parser.format_help()


#*********************************************************************************************************
# Internal global variables
MOTION_COMMANDS =   ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03'] # Motion gCode commands definition
RAPID_MOVES =       ['G0', 'G00']                                        # Rapid moves gCode commands definition
SUPPRESS_COMMANDS = ['G99', 'G98', 'G80']      #COMMIT DOCS added 'G99' to SUPPRESS_COMMANDS to fix error in unfolding G83 cycle                                 # These commands are ignored by commenting them out
COMMAND_SPACE = " "
CURRENT_X = 0 # Global variable storing current position
CURRENT_Y = 0
CURRENT_Z = 0
#*********************************************************************************************************


# to distinguish python built-in open function from the one declared below
if open.__module__  in ['__builtin__','io']:
  pythonopen = open


def processArguments(argstring):

  global OUTPUT_HEADER
  global OUTPUT_COMMENTS
  global OUTPUT_LINE_NUMBERS
  global SHOW_EDITOR
  global PRECISION
  global PREAMBLE
  global POSTAMBLE
  global TRANSLATE_DRILL_CYCLES

  try:
    args = parser.parse_args(shlex.split(argstring))
    if args.no_header:
      OUTPUT_HEADER = False
    if args.header:
      OUTPUT_HEADER = True
    if args.no_comments:
      OUTPUT_COMMENTS = False
    if args.comments:
      OUTPUT_COMMENTS = True
    if args.no_line_numbers:
      OUTPUT_LINE_NUMBERS = False
    if args.line_numbers:
      OUTPUT_LINE_NUMBERS = True
    if args.no_show_editor:
      SHOW_EDITOR = False
    if args.show_editor:
      SHOW_EDITOR = True
    PRECISION = args.precision
    if args.preamble is not None:
      PREAMBLE = args.preamble
    if args.postamble is not None:
      POSTAMBLE = args.postamble
    if args.no_translate_drill:
      TRANSLATE_DRILL_CYCLES = False
    if args.translate_drill:
      TRANSLATE_DRILL_CYCLES = True

  except:
    return False

  return True


# For debug...
def dump(obj):
    for attr in dir(obj):
        print("obj.%s = %s" % (attr, getattr(obj, attr)))


def export(objectslist, filename, argstring):

  if not processArguments(argstring):
    return None

  global UNITS
  global MOTION_MODE

  print("Post Processor: " + __name__ +" postprocessing...")
  gcode = ""

  # write header
  if OUTPUT_HEADER:
    gcode += linenumber() + "(Exported by FreeCAD)\n"
    gcode += linenumber() + "(Post Processor: " + __name__ +")\n"
    gcode += linenumber() + "(Output Time:"+str(datetime.datetime.now())+")\n"

  # Write the preamble
  if OUTPUT_COMMENTS:
    gcode += linenumber() + "(begin preamble)\n"
  for line in PREAMBLE.splitlines(True):
    gcode += linenumber() + line
  # verify if PREAMBLE have changed MOTION_MODE or UNITS
  if 'G90' in PREAMBLE:
    MOTION_MODE = 'G90'
  elif 'G91' in PREAMBLE:
    MOTION_MODE = 'G91'
  else:
    gcode += linenumber() + MOTION_MODE + "\n"
  if 'G21' in PREAMBLE:
    UNITS = 'G21'
  elif 'G20' in PREAMBLE:
    UNITS = 'G20'
  else:
    gcode += linenumber() + UNITS + "\n"

  for obj in objectslist:
    # Debug...
    #print("\n" + "*"*70)
    #dump(obj)
    #print("*"*70 + "\n")
    if not hasattr(obj,"Path"):
      print("The object " + obj.Name + " is not a path. Please select only path and Compounds.")
      return

    # do the pre_op
    if OUTPUT_COMMENTS: gcode += linenumber() + "(begin operation: " + obj.Label + ")\n"
    for line in PRE_OPERATION.splitlines(True):
        gcode += linenumber() + line

    # Parse the op
    gcode += parse(obj)

    # do the post_op
    if OUTPUT_COMMENTS: gcode += linenumber() + "(finish operation: " + obj.Label + ")\n"
    for line in POST_OPERATION.splitlines(True):
        gcode += linenumber() + line

  #do the post_amble
  if OUTPUT_COMMENTS: gcode += linenumber() + "(begin postamble)\n"
  for line in POSTAMBLE.splitlines(True):
    gcode += linenumber() + line

  #show the gCode result dialog
  if FreeCAD.GuiUp and SHOW_EDITOR:
    dia = PostUtils.GCodeEditorDialog()
    dia.editor.setText(gcode)
    result = dia.exec_()
    if result:
      final = dia.editor.toPlainText()
    else:
      final = gcode
  else:
    final = gcode

  print("done postprocessing.")

  #write the file
  gfile = pythonopen(filename,"w")
  gfile.write(gcode)
  gfile.close()


def linenumber():
  global LINENR
  global LINEINCR
  if OUTPUT_LINE_NUMBERS == True:
    s = "N" + str(LINENR) + " "
    LINENR += LINEINCR
    return s
  return ""


def format_outstring(strTbl):
  global COMMAND_SPACE
  # construct the line for the final output
  s = ""
  for w in strTbl:
    s += w + COMMAND_SPACE
  s = s.strip()
  return s


def parse(pathobj):

  global DRILL_RETRACT_MODE
  global MOTION_MODE
  global CURRENT_X
  global CURRENT_Y
  global CURRENT_Z

  out = ""
  lastcommand = None
  precision_string = '.' + str(PRECISION) +'f'

  params = ['X','Y','Z','A','B','C','I','J','K','F','S','T','Q','R','L','P']

  if hasattr(pathobj,"Group"): # We have a compound or project.
    if OUTPUT_COMMENTS: out += linenumber() + "(compound: " + pathobj.Label + ")\n"
    for p in pathobj.Group:
      out += parse(p)
    return out

  else: # parsing simple path
    if not hasattr(pathobj,"Path"): #groups might contain non-path things like stock.
      return out

    if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n"

    for c in pathobj.Path.Commands:
      outstring = []
      command = c.Name

      outstring.append(command)

      # if modal: only print the command if it is not the same as the last one
      if MODAL == True:
        if command == lastcommand:
          outstring.pop(0)

      # Now add the remaining parameters in order
      for param in params:
        if param in c.Parameters:
          if param == 'F':
            if command not in RAPID_MOVES:
              outstring.append(param + format(c.Parameters['F'] * SPEED_MULTIPLIER, '.2f'))
          elif param == 'T':
            outstring.append(param + str(c.Parameters['T']))
          else:
            outstring.append(param + format(c.Parameters[param], precision_string))

      # store the latest command
      lastcommand = command

        # Memorise la position courante pour calcul des mouvements relatis et du plan de retrait
      if command in MOTION_COMMANDS:
        if 'X' in c.Parameters:
          CURRENT_X = c.Parameters['X']
        if 'Y' in c.Parameters:
          CURRENT_Y = c.Parameters['Y']
        if 'Z' in c.Parameters:
          CURRENT_Z = c.Parameters['Z']

      if command in ('G98', 'G99'):
        DRILL_RETRACT_MODE = command

      if command in ('G90', 'G91'):
        MOTION_MODE = command

      if TRANSLATE_DRILL_CYCLES:
        if command in ('G81', 'G82', 'G83'):
          out += drill_translate(outstring, command, c.Parameters)
          # Efface la ligne que l'on vient de translater
          del(outstring[:])
          outstring = []

      # Check for Tool Change:
      if command in ('M6', 'M06'):
        if OUTPUT_COMMENTS: out += linenumber() + "(begin toolchange)\n"
        if not OUTPUT_TOOL_CHANGE:
          outstring[0] = "(" + outstring[0]
          outstring[-1] = outstring[-1] + ")"
        else:
          for line in TOOL_CHANGE.splitlines(True):
            out += linenumber() + line

      if command == "message":
        if OUTPUT_COMMENTS == False:
          out = []
        else:
          outstring.pop(0) #remove the command

      if command in SUPPRESS_COMMANDS:
        outstring[0] = "(" + outstring[0]
        outstring[-1] = outstring[-1] + ")"

      #prepend a line number and append a newline
      if len(outstring) >= 1:
          out += linenumber() + format_outstring(outstring) + "\n"

  return out


def drill_translate(outstring, cmd, params):
  global DRILL_RETRACT_MODE
  global MOTION_MODE
  global CURRENT_X
  global CURRENT_Y
  global CURRENT_Z

  strFormat = '.' + str(PRECISION) +'f'

  trBuff = ""

  if OUTPUT_COMMENTS: # Comment the original command
    outstring[0] = "(" + outstring[0]
    outstring[-1] = outstring[-1] + ")"
    trBuff += linenumber() + format_outstring(outstring) + "\n"

  # Conversion du cycle
  # Pour l'instant, on gere uniquement les cycles dans le plan XY (G17)
  # les autres plans ZX (G18) et YZ (G19) ne sont pas traites : Calculs sur Z uniquement.
  if MOTION_MODE == 'G90': # Deplacements en coordonnees absolues
    drill_X = params['X']
    drill_Y = params['Y']
    drill_Z = params['Z']
    RETRACT_Z = params['R'] #RETRACT_Z_SMALL = params['R'] # added in the mod to enable small retracts with G83
  else: # G91 Deplacements relatifs
    drill_X = CURRENT_X + params['X']
    drill_Y = CURRENT_Y + params['Y']
    drill_Z = CURRENT_Z + params['Z']
    RETRACT_Z = CURRENT_Z + params['R']

  if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z >= RETRACT_Z:
    RETRACT_Z = CURRENT_Z 

  # Recupere les valeurs des autres parametres
  drill_Speed = params['F'] * SPEED_MULTIPLIER
  if cmd == 'G83':
    drill_Step = params['Q']
  elif cmd == 'G82':
    drill_DwellTime = params['P']

  if MOTION_MODE == 'G91':
    trBuff += linenumber() + "G90" + "\n" # Force des deplacements en coordonnees absolues pendant les cycles
#COMMIT DOCS Don't think this applies to G99 so propose a conditional. Don't know what to do for safe height move though, or even if that's needed 
  # Mouvement(s) preliminaire(s))
  if CURRENT_Z < RETRACT_Z and DRILL_RETRACT_MODE == 'G98': #COMMIT DOCS added condition
    trBuff += linenumber() + 'G0 Z' + format(RETRACT_Z, strFormat) + "\n"
  trBuff += linenumber() + 'G0 X' + format(drill_X, strFormat) + ' Y' + format(drill_Y, strFormat) + "\n"
  if CURRENT_Z > RETRACT_Z and DRILL_RETRACT_MODE == 'G98': #COMMIT DOCS added condition
    trBuff += linenumber() + 'G0 Z' + format(CURRENT_Z, strFormat) + "\n"

  # Mouvement de percage
  if cmd in ('G81', 'G82'):
    trBuff += linenumber() + 'G1 Z' + format(drill_Z, strFormat) + ' F' + format(drill_Speed, '.2f') + "\n"
    # Temporisation eventuelle
    if cmd == 'G82':
      trBuff += linenumber() + 'G4 P' + str(drill_DwellTime) + "\n"
    # Sortie de percage
    trBuff += linenumber() + 'G0 Z' + format(RETRACT_Z, strFormat) + "\n"
  else: # 'G83'
    if DRILL_RETRACT_MODE == 'G98':#COMMIT DOCS added condition
      next_Stop_Z = RETRACT_Z - drill_Step
    elif DRILL_RETRACT_MODE == 'G99': #COMMIT DOCS added condition
      next_Stop_Z = CURRENT_Z - drill_Step #COMMIT DOCS added alternative point#
    while 1:
      if next_Stop_Z > drill_Z:
        trBuff += linenumber() + 'G1 Z' + format(next_Stop_Z, strFormat) + ' F' + format(drill_Speed, '.2f') + "\n"
        if DRILL_RETRACT_MODE == 'G98': #COMMIT DOCS added condition
          trBuff += linenumber() + 'G0 Z' + format(RETRACT_Z, strFormat) + "\n"
        elif DRILL_RETRACT_MODE == 'G99': #COMMIT DOCS added condition  
          trBuff += linenumber() + 'G0 Z' + format(next_Stop_Z + RETRACT_Z, strFormat) + "\n" #COMMIT DOCS adapted retract height to match G99
        next_Stop_Z -= drill_Step
      else:
        trBuff += linenumber() + 'G1 Z' + format(drill_Z, strFormat) + ' F' + format(drill_Speed, '.2f') + "\n"
        if DRILL_RETRACT_MODE == 'G98': #COMMIT DOCS added condition
          trBuff += linenumber() + 'G0 Z' + format(RETRACT_Z, strFormat) + "\n"
        elif DRILL_RETRACT_MODE == 'G99': #COMMIT DOCS added condition
          trBuff += linenumber() + 'G0 Z' + format(CURRENT_Z, strFormat) + "\n" #COMMIT DOCS adapted retract to original rapids height before drilling operation in Z
        break

  if MOTION_MODE == 'G91':
    trBuff += linenumber() + 'G91' # Restore le mode de deplacement relatif

  return trBuff


print(__name__ + ": gCode postprocessor loaded.")
User avatar
Gauthier
Posts: 105
Joined: Fri Jul 04, 2014 10:00 am
Location: Audenge, France

Re: GRBL new post processor with drill cycles G81..G83

Postby Gauthier » Fri Jan 10, 2020 7:13 pm

Hi @smerrett79,

Warning, the grbl_81_post included in the 0.18.4 FreeCAD's version is not the last version of the post processor.
In FreeCAD v0.19, grbl_81_post have been renamed and replace the old grbl_post. It also have some updates who made it evolve...

However, the resulting moves it give are correct regarding the G83 definition and the post processor is correct for this point.
It is the value of your retract plane (R parameter: R0.1000) that is not correct.
The G83 retract plan is a fixed Z height wich is differently defined with G98 or G99, but it's a fixed height. In your case, you defined it at 0.1mm. Your stock is 15 mm height and you can't begin to drill at 14.7mm (the first step of your cycle) and go up to 0.1mm!
You need to define the retract plane at least at 15mm => R15.0000.

@++;
Gauthier.