-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdrill.py
executable file
·219 lines (185 loc) · 6.56 KB
/
drill.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/usr/bin/env python
import argparse
import datetime
import sys
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
def __repr__(self):
return self.__str__()
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def dist(self, other):
return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
def __lt__(self, other):
return False
def __hash__(self):
return hash((self.x, self.y))
def parse_number(number: str):
if number == "":
return 0
return float(number)
def parse_command(line: str):
command = {}
last_point = "?"
for ch in line:
if ch.isalpha():
last_point = ch
command[ch] = ""
else:
command[last_point] += ch
coord = {}
for key in command:
if key in ["X", "Y", "I", "J"]:
coord[key] = parse_number(command[key])
p = Point(coord.get("X", 0), coord.get("Y", 0))
code = "G" + command.get("G", "00") # default to travel
return {code: p}
def parse_drl(file_path: str):
drill_mode = False
tools = {}
selected_tool = None
with open(file_path, "r") as file:
line_no = 0
for line in file:
line_no += 1
try:
line = line.split(";")[0].strip()
if len(line) == 0:
continue
if line.startswith("T"):
if "C" in line:
params = line.split("C")
t = int(params[0][1:])
tools[t] = {
"diameter": float(params[1]),
"points": [],
"paths": [],
}
else:
selected_tool = int(line[1:])
continue
if line.startswith("G05"):
drill_mode = True
continue
if line.startswith("M15"):
# Plunge
tools[selected_tool]["paths"].append({"M15": ""})
continue
if line.startswith("M16"):
# Retract
tools[selected_tool]["paths"].append({"M16": ""})
continue
if line.startswith(("G00", "G01")):
drill_mode = False
parsed = parse_command(line)
tools[selected_tool]["paths"].append(parsed)
continue
if line.startswith(("X", "Y")) and drill_mode:
_, p = parse_command(line).popitem()
tools[selected_tool]["points"].append(p)
continue
except Exception as e:
print(f"Error parsing line({line_no}): {line}")
print(e)
raise e
return tools
def sort_points(points: list, entry: Point):
if len(points) == 0:
return []
sorted_points = []
points = points.copy()
current = entry
while len(points) > 0:
next_point = min(points, key=lambda p: current.dist(p))
sorted_points.append(next_point)
points.remove(next_point)
current = next_point
return sorted_points
def tool_change(t: int, d: float):
return f"T{t} M06; {d}mm\n"
def generate_gcode(tools, args, file_path=None):
output = f"""; Generated by {sys.argv[0]} on {datetime.datetime.now()}
G21; Set units to mm
G90; Set absolute positioning
G17; Set XY plane
G94; Set feedrate to mm/min
G40; Disable tool radius compensation
G49; Disable tool length offset
G01 F{args.feed}; Set feedrate to {args.feed} mm/min
M03 S{args.rpm}; Start spindle at {args.rpm} RPM
"""
output += f"G00 Z{args.retract}\n"
for t, tool in tools.items():
output += tool_change(t, tool["diameter"])
for p in tool["points"]:
output += f"G00 X{p.x} Y{p.y}\n"
output += f"G00 Z{args.start}\n"
output += f"G01 Z{args.end}\n"
output += f"G00 Z{args.retract}\n"
for path in tool["paths"]:
for code in path:
if code == "M15":
output += f"G00 Z{args.start}\n"
output += f"G01 Z{args.end}\n"
elif code == "M16":
output += f"G00 Z{args.retract}\n"
else:
output += f"{code} X{path[code].x} Y{path[code].y}\n"
output += "M30; End of program\n"
print(output)
file_path = file_path or args.output
with open(file_path, "w") as file:
file.write(output)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Convert a drill file to GCode")
parser.add_argument("-i", "--input", help="Input file path")
parser.add_argument("-o", "--output", help="Output file path")
parser.add_argument(
"-r", "--retract", type=float, default=20.0, help="Retract height"
)
parser.add_argument(
"-s", "--start", type=float, default=1.0, help="Cut start height"
)
parser.add_argument("-e", "--end", type=float, default=-1.65, help="Cut end height")
parser.add_argument(
"-x", "--entry", type=float, nargs=2, default=(0, 0), help="Entry point"
)
parser.add_argument(
"-f", "--feed", type=float, default=100.0, help="Feedrate in mm/min"
)
parser.add_argument(
"-rpm", "--rpm", type=float, default=10000.0, help="Spindle speed in RPM"
)
parser.add_argument(
"-sp",
"--split",
type=bool,
default=True,
help="Split each tool into its own file",
)
args = parser.parse_args()
tools = parse_drl(args.input)
for tool in tools:
print(f"Tool: {tool}")
print(f"Diameter: {tools[tool]['diameter']}")
print(f"Points({len(tools[tool]['points'])}): {tools[tool]['points']}")
print(f"Paths({len(tools[tool]['paths'])}): {tools[tool]['paths']}")
# sort points
for tool in tools:
tools[tool]["points"] = sort_points(tools[tool]["points"], Point(*args.entry))
if args.split:
path = args.output.split(".")
suffix = path[-1]
file_path = path[0]
for tool in tools:
generate_gcode({tool: tools[tool]}, args, f"{file_path}_T{tool}.{suffix}")
else:
generate_gcode(tools, args)