From e6d2b3c9af01a263e32774b82f0aa1e718fd0206 Mon Sep 17 00:00:00 2001 From: visdml Date: Thu, 22 Feb 2024 18:10:23 +0100 Subject: [PATCH] Inital commit and working draft --- KOSY_post.py | 333 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 KOSY_post.py diff --git a/KOSY_post.py b/KOSY_post.py new file mode 100644 index 0000000..25eeab5 --- /dev/null +++ b/KOSY_post.py @@ -0,0 +1,333 @@ +# ***************************************************************************** +# * Copyright (c) 2024 Marcel Dahl * +# * * +# * 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 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * FreeCAD 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 FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# ****************************************************************************/ +"""Postprocessor to output real GCode for Max Computer GmbH nccad7.5.""" +import FreeCAD +import Path.Post.Utils as PostUtils +import PathScripts.PathUtils as PathUtils +import datetime + + +TOOLTIP = """ +This is a postprocessor file for the Path workbench. +It is used to output real G-code suitable for the KOSY CNC devices. + +Some G and M codes are not supported. Please review result. + +Preconditions: +metric values (check settings) +Entered filename without suffix. Will be stored as .knc (nccad file extension for gcode) +""" + +MACHINE_NAME = """Max Computer GmbH nccad MCS/KOSY""" + +# gCode for changing tools +# M01 ; Displays and waits for user interaction +TOOL_CHANGE = """G77 ; Move to release position +M10 O6.0 ; Stop spindle +M01 Insert tool TOOL +G76 ; Move to reference point / WNP +M10 O6.1 ; Start spindle / relais 6 on""" + + +# gCode finishing the program +POSTAMBLE = """G77 ; Move to release position fast +M10 O6.0 ; Stop spindle relais 6 off +G99 ;end program module""" + + +# gCode header with information about CAD-software, post-processor +# and date/time +if FreeCAD.ActiveDocument: + cam_file = FreeCAD.ActiveDocument.FileName +else: + cam_file = "" + +HEADER = """;Exported by FreeCAD +;Post Processor: {} +;CAM file: {} +;Output Time: {} +""".format( + __name__, cam_file, str(datetime.datetime.now()) +) + +# *************************************************************************** +# * Internal global variables +# *************************************************************************** +RAPID_MOVES = ["G0", "G00"] # Rapid moves gCode commands definition +# Global variables storing current position +CURRENT_X = 0 +CURRENT_Y = 0 +CURRENT_Z = 0 +CURRENT_F = 0 + + +def export(objectslist, filename, argstring): + """Export the list of objects into a filename. + + Parameters + ---------- + objectslists: list + List of objects. + + filename: str + Name of the output file without + """ + gcode = HEADER + + for obj in objectslist: + gcode+= parse(obj) + gcode += "\n" + + gcode += POSTAMBLE + "\n" + + # Open editor window + if FreeCAD.GuiUp: + dia = PostUtils.GCodeEditorDialog() + dia.editor.setText(gcode) + result = dia.exec_() + if result: + gcode = dia.editor.toPlainText() + + # Save to file + if filename != "-": + nccadFileEnding = ".knc" + if filename.endswith(".nc"): # freecad file suffix + filename = nccadFileEnding.join(filename.rsplit(".nc", 1)) + gfile = open(filename, "w") + gfile.write(gcode) + gfile.close() + + return filename + +def parse(pathobj): + + global CURRENT_X + global CURRENT_Y + global CURRENT_Z + global CURRENT_F + + out = "" + lastcommand = None + precision_string = ".2f" # Kosy support 0.01 mm steps + + params = [ + "X", + "Y", + "Z", + "A", + "B", + "C", + "U", + "V", + "W", + "I", + "J", + "K", + "F", + "S", + "T", + "Q", + "R", + "L", + "P", + ] + +# see KOSY help file NC-Kurzbefehlliste + validCommands = [ + "G0", + "G1", + "G2", + "G3", + "G20", + "G24", + "G54", + "G60", + "G61", + "G74", + "G76", + "G77", + "G79", + # G80 start manufacture specific custom g-codes + "G81", + "G87", + # G89 end custom g-codes + "G88", + "G89", + "G90", + # "G91", # KOSY valid, but not checked if input of postprocessor / Freecad delivers relative points + "G98", + "G99", + "M1", + #"M2", + #"M3", + #"M4", + "M5", + "M10", + "M20", + "M25", + "M30", + "M35", + ] + + if hasattr(pathobj, "Group"): # We have a compound or project. + out += "(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 + + for c in PathUtils.getPathWithPlacement(pathobj).Commands: + outstring = [] + command = c.Name + + if command[0] == ";(" : # command is a comment + out += command + "\n" + continue + + if not command in validCommands: + out += ";(Unsupported command <"+ command + "> for KOSY maschine. Check simulation! )\n" + continue + + # print the command if it is not the same as the last one + if command == lastcommand: + outstring.append(" ") # align / multiple spaces are ignored by machine + else: + outstring.append(command) + + # 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: + speed = c.Parameters[param] + if speed > 0.0 and speed != CURRENT_F: + CURRENT_F = speed + # Multiply F parameter value by 10, + # FreeCAD = mm/s, nccad = 1/10 mm/s + # speed *= 10 + # Add command parameters and values and round float + # as nccad9 does not support exponents + outstring.append( param + format( float(speed), + precision_string, + ) + ) + elif param in ["T", "H", "S"]: + outstring.append(param + str(int(c.Parameters[param]))) + elif param in ["D", "P", "L"]: + outstring.append(param + str(c.Parameters[param])) + elif param in ["A", "B", "C"]: + outstring.append( + param + format(c.Parameters[param], precision_string) + ) + elif param == "K": # 2,5D KOSY doesnt support milling Z axis + continue + else: # [X, Y, Z, U, V, W, I, J, R, Q] + pos = format( float(c.Parameters[param]), precision_string ) + skip = False + + # improve code with + # skip = sameCoordinate(pos, CURRENT_X) + + + if param == "X": + if pos == CURRENT_X: + skip = True # same X coordinate + else: + CURRENT_X = pos # store new position + + if param == "Y": + if pos == CURRENT_Y: + skip = True # same coordinate + else: + CURRENT_Y = pos # store new position + + if param == "Z": + if pos == CURRENT_Z: + skip = True # same coordinate + else: + CURRENT_Z = pos # store new position + + if not skip or ( command in ["G2", "G3"] and param != "Z"): # while rotating movements KOSY needs all x y parameter + outstring.append(param + format( pos ) + ) + + # store the latest command + lastcommand = command + +# TODO +# if command in ("G98", "G99"): +# DRILL_RETRACT_MODE = command +# +# +# if TRANSLATE_DRILL_CYCLES: +# if command in ("G81", "G82", "G83"): +# out += drill_translate(outstring, command, c.Parameters) +# # Erase the line we just translated +# outstring = [] +# TODO +# if SPINDLE_WAIT > 0: + # if command in ("M3", "M03", "M4", "M04"): + # out += format_outstring(outstring) + "\n" + # out += ( + # linenumber() + # + format_outstring(["G4", "P%s" % SPINDLE_WAIT]) + # + "\n" + # ) + # outstring = [] + + # # Check for Tool Change: + # if command in ("M6", "M06"): + # if OUTPUT_COMMENTS: + # out += "(Begin toolchange)\n" + # if not OUTPUT_TOOL_CHANGE: + # outstring.insert(0, "(") + # outstring.append(")") + # else: + # for line in TOOL_CHANGE.splitlines(True): + # out += line + + # prepend a line number and append a newline + if len(outstring) >= 1: + out += format_outstring(outstring) + "\n" + + return out + +# construct the line for the final output +def format_outstring(strTable): + s = "" + for w in strTable: + s += w + " " + s = s.rstrip() + return s + +#store into global variable? how by reference? +def sameCoordinate(position, lastPosition): + if position == lastPosition: + return True + else: + lastPosition = position + return False +