cyclone-pcb-factory/Software/CycloneHost/GcodeParser.py

201 lines
5.8 KiB
Python

#!/usr/bin/python
# AUTHOR:
# Carlosgs (http://carlosgs.es)
# LICENSE:
# Attribution - Share Alike - Creative Commons (http://creativecommons.org/licenses/by-sa/3.0/)
#
# DISCLAIMER:
# This software is provided "as is", and you use the software at your own risk. Under no
# circumstances shall Carlosgs be liable for direct, indirect, special, incidental, or
# consequential damages resulting from the use, misuse, or inability to use this software,
# even if Carlosgs has been advised of the possibility of such damages.
#
# CREDIT:
# Based on Etch_Z_adjust.1.8.py from http://www.cnczone.com/forums/pcb_milling/82628-cheap_simple_height-probing.html (multiple authors)
# Begin modules
import os.path
# End modules
def parseGcodeRaw(filePath, etch_definition = 0): # Gcode parser from Etch_Z_adjust.1.8.py (modified by Carlosgs to output toolpaths)
gcode_size = (0,0)
gcode_origin = (0,0)
travel_moves = []
etch_moves = []
if os.path.isfile(filePath) == False :
return etch_moves, travel_moves, gcode_origin, gcode_size
gcode = open(filePath, "r")
# Height to consider etching
# etch_definition = 0
# Check for max and min values in the gcode file
is_first_X = True
is_first_Y = True
is_first_Z = True
G_dest = 0
X_dest = 0
Y_dest = 0
Z_dest = 10
path = []
def get_num(line,char_ptr,num_chars):
char_ptr=char_ptr+1
numstr = ''
good = '-.0123456789'
while char_ptr < num_chars:
digit = line[char_ptr]
if good.find(digit) != -1:
numstr = numstr + digit
char_ptr = char_ptr + 1
else: break
return numstr
def test_X(X_min, X_max):
if X_dest < X_min : X_min = X_dest
elif X_dest > X_max : X_max = X_dest
return X_min, X_max
def test_Y(Y_min, Y_max):
if Y_dest < Y_min : Y_min = Y_dest
elif Y_dest > Y_max : Y_max = Y_dest
return Y_min, Y_max
etchMove = False
currentLine = 0.0
lines = gcode.readlines()
totalLines = len(lines)
for line in lines:# check each line
currentLine = currentLine + 1
#print "({0:.1f}%)".format((currentLine / totalLines)*100), "Reading:", line[:-1]
X_start = X_dest
Y_start = Y_dest
Z_start = Z_dest
# check each character
char_ptr = 0
num_chars= len(line)
while char_ptr < num_chars:
char = line[char_ptr]
if '(;'.find(char) != -1:
break
elif char == 'G' :
G_dest = int(get_num(line,char_ptr,num_chars))
elif char == 'X' :
X_dest = float(get_num(line,char_ptr,num_chars))
elif char == 'Y' :
Y_dest = float(get_num(line,char_ptr,num_chars))
elif char == 'Z' :
Z_dest = float(get_num(line,char_ptr,num_chars))
char_ptr = char_ptr + 1
if G_dest == 0 or G_dest == 1 :
if Z_dest < etch_definition: # if the line is an etch move, then replace the line with an etch call
#line = 'O200 call [%.4f] [%.4f] [%.4f] [%.4f]\n' % (X_start, Y_start, X_dest, Y_dest)
if etchMove == False :
travel_moves.append(path)
path = []
etchMove = True # Set etch mode
path.append([X_dest,Y_dest,Z_dest])
# and now check for max and min X and Y values
if is_first_X == True :
X_min = X_dest
X_max = X_dest
is_first_X = False
else : (X_min, X_max) = test_X(X_min, X_max)
if is_first_Y == True :
Y_min = Y_dest
Y_max = Y_dest
is_first_Y = False
else : (Y_min, Y_max) = test_Y(Y_min, Y_max)
else :
if etchMove == True :
etch_moves.append(path)
path = []
etchMove = False # Set travel mode
path.append([X_dest,Y_dest,Z_dest])
#file_out.append(line)
if is_first_X == False :
# then there were etch moves so get to work!
gcode_size = (X_max - X_min, Y_max - Y_min)
gcode_origin = (X_min, Y_min)
print "Gcode XY origin:",str(gcode_origin)
print "Gcode XY length:",str(gcode_size)
else : print "No etch moves found!"
gcode.close()
return etch_moves, travel_moves, gcode_origin, gcode_size
def optimize(etch_moves_in, origin=[0,0], travel_height = 5): # Optimizes the toolpath using closest neighbour (author: Carlosgs)
etch_moves = []
travel_moves = []
if len(etch_moves_in) == 0 :
return etch_moves, travel_moves
travel_moves = []
toolPosition = [origin[0], origin[1], travel_height]
minDistance = 1e9
while len(etch_moves_in) > 0 : # While there are remaining moves
closest = [1e9,1e9]
distance = 1e9
closestMove_i = 0
i = 0
reverse = 0
for path in etch_moves_in : # Find the one that begins more close to the position of our tool
firstPoint = path[0]
distance = (toolPosition[0]-firstPoint[0])**2 + (toolPosition[1]-firstPoint[1])**2 # We only check XY
if distance < closest :
closest = distance
closestMove_i = i
else : # We also consider that paths can be made in reverse
firstPoint = path[-1] # We check the last point first
distance = (toolPosition[0]-firstPoint[0])**2 + (toolPosition[1]-firstPoint[1])**2 # We only check XY
if distance < closest :
closest = distance
closestMove_i = i
reverse = 1 # Flag set to reverse the path
i = i + 1
path = etch_moves_in[closestMove_i] # Select the closest that has been found
if reverse :
path = path[::-1] # If the closest one was from the end, we reverse the path
#print "Reverse!"
firstPoint = path[0]
if distance > 0.01 : # This will join etching moves closer than 0.01 mm
travel_moves.append([toolPosition, [firstPoint[0], firstPoint[1], travel_height]]) # Travel to the initial point of the etching
if distance < minDistance :
minDistance = distance
etch_moves.append(path) # Do the etching
etch_moves_in.pop(closestMove_i) # Remove the move from the list, it has been done!
toolPosition = path[-1] # Set our endpoint as the initial one for the next move
print "Minimum travel distance:", minDistance
travel_moves.append([toolPosition, [origin[0], origin[1], travel_height]]) # Return to the origin
return etch_moves, travel_moves