@@ -18,12 +18,14 @@ for pygcode_lib_type in ('installed_lib', 'relative_lib'):
1818 from pygcode import Machine , Mode , Line
1919 from pygcode import GCodeArcMove , GCodeArcMoveCW , GCodeArcMoveCCW
2020 from pygcode import GCodeCannedCycle
21+ from pygcode import GCodeRapidMove , GCodeStopSpindle , GCodeAbsoluteDistanceMode
2122 from pygcode import split_gcodes
2223 from pygcode import Comment
2324 from pygcode .transform import linearize_arc , simplify_canned_cycle
2425 from pygcode .transform import ArcLinearizeInside , ArcLinearizeOutside , ArcLinearizeMid
2526 from pygcode .gcodes import _subclasses
2627 from pygcode import utils
28+ from pygcode .exceptions import MachineInvalidState
2729
2830 except ImportError :
2931 import sys , os , inspect
@@ -65,7 +67,9 @@ def arc_lin_method_type(value):
6567
6668def word_list_type (value ):
6769 """
68- :return: [Word('G73'), Word('G89'), ... ]
70+ Convert csv string list into Word instances.
71+ >>> word_list_type("G73,G89") == set([Word('G73'), Word('G89')])
72+ :return: set of Word instances
6973 """
7074 canned_code_words = set ()
7175 for word_str in re .split (r'\s*,\s*' , value ):
@@ -82,29 +86,29 @@ DEFAULT_CANNED_CODES = ','.join(str(w) for w in sorted(c.word_key for c in _subc
8286
8387# --- Create Parser
8488parser = argparse .ArgumentParser (
85- description = "Normalize gcode for machine consistency when using different CAM software"
89+ description = "Normalize gcode for machine consistency when using different CAM software. "
8690)
8791parser .add_argument (
8892 'infile' , type = argparse .FileType ('r' ),
89- help = "gcode file to normalize" ,
93+ help = "Gcode file to normalize. " ,
9094)
9195
9296parser .add_argument (
9397 '--singles' , '-s' , dest = 'singles' ,
9498 action = 'store_const' , const = True , default = False ,
95- help = "only output one command per gcode line" ,
99+ help = "Only output one command per gcode line. " ,
96100)
97101parser .add_argument (
98102 '--full' , '-f' , dest = 'full' ,
99103 action = 'store_const' , const = True , default = False ,
100- help = "output full commands, any modal parameters will be acompanied with "
101- "the fully qualified gcode command" ,
104+ help = "Output full commands, any modal parameters will be acompanied with "
105+ "the fully qualified gcode command. " ,
102106)
103107
104108# Machine
105109parser .add_argument (
106110 '--machine_mode' , '-mm' , dest = 'machine_mode' , default = DEFAULT_MACHINE_MODE ,
107- help = "Machine's startup mode as gcode (default: '%s')" % DEFAULT_MACHINE_MODE ,
111+ help = "Machine's startup mode as gcode (default: '%s'). " % DEFAULT_MACHINE_MODE ,
108112)
109113
110114# Arc Linearizing
@@ -117,20 +121,20 @@ group = parser.add_argument_group(
117121group .add_argument (
118122 '--arc_linearize' , '-al' , dest = 'arc_linearize' ,
119123 action = 'store_const' , const = True , default = False ,
120- help = "convert G2,G3 commands to a series of linear interpolations (G1 codes)" ,
124+ help = "Convert G2,G3 commands to a series of linear interpolations (G1 codes). " ,
121125)
122126group .add_argument (
123127 '--arc_lin_method' , '-alm' , dest = 'arc_lin_method' ,
124128 type = arc_lin_method_type , default = DEFAULT_ARC_LIN_METHOD ,
125129 help = "Method of linearizing arcs, i=inner, o=outer, m=mid. List 2 "
126130 "for <cw>,<ccw>, eg 'i,o'. 'i' is equivalent to 'i,i'. "
127- "(default: '%s')" % DEFAULT_ARC_LIN_METHOD ,
131+ "(default: '%s'). " % DEFAULT_ARC_LIN_METHOD ,
128132 metavar = '{i,o,m}[,{i,o,m}]' ,
129133)
130134group .add_argument (
131135 '--arc_precision' , '-alp' , dest = 'arc_precision' , type = float , default = DEFAULT_PRECISION ,
132- help = "maximum positional error when creating linear interpolation codes "
133- "(default: %g)" % DEFAULT_PRECISION ,
136+ help = "Maximum positional error when creating linear interpolation codes "
137+ "(default: %g). " % DEFAULT_PRECISION ,
134138)
135139
136140#parser.add_argument(
@@ -149,41 +153,73 @@ group = parser.add_argument_group(
149153group .add_argument (
150154 '--canned_expand' , '-ce' , dest = 'canned_expand' ,
151155 action = 'store_const' , const = True , default = False ,
152- help = "Expand canned cycles into basic linear movements, and pauses" ,
156+ help = "Expand canned cycles into basic linear movements, and pauses. " ,
153157)
154158group .add_argument (
155159 '--canned_codes' , '-cc' , dest = 'canned_codes' ,
156160 type = word_list_type , default = DEFAULT_CANNED_CODES ,
157- help = "List of canned gcodes to expand, (default is '%s')" % DEFAULT_CANNED_CODES ,
161+ help = "List of canned gcodes to expand, (default is '%s')." % DEFAULT_CANNED_CODES ,
162+ )
163+
164+ # Finalize Code
165+ group = parser .add_argument_group (
166+ "Final Machine Actions" ,
167+ "standardize what's done at the end of a gcode program."
168+ )
169+ group .add_argument (
170+ '--zero_xy' , '-zxy' , dest = "zero_xy" ,
171+ action = 'store_const' , const = True , default = False ,
172+ help = "On completion, move straight up to rapid_safety_height, "
173+ "then across to X0 Y0." ,
174+ )
175+ group .add_argument (
176+ '--zero_z' , '-zz' , dest = "zero_z" ,
177+ action = 'store_const' , const = True , default = False ,
178+ help = "On completion, move down to Z0 (done after zero_xy, if set)." ,
179+ )
180+ group .add_argument (
181+ '--rapid_safety_height' , '-rsh' , dest = "rapid_safety_height" ,
182+ type = float , default = None ,
183+ help = "Z value to move to before traversing workpiece (if not set, max "
184+ "value will be attempted)." ,
185+ )
186+ group .add_argument (
187+ '--spindle_off' , '-so' , dest = "spindle_off" ,
188+ action = 'store_const' , const = True , default = False ,
189+ help = "On completion, turn spindle off." ,
158190)
159191
160192# Removing non-functional content
161193group = parser .add_argument_group (
162194 "Removing Content" ,
163- "options for the removal of content"
195+ "options for the removal of content. "
164196)
165197group .add_argument (
166198 '--rm_comments' , '-rc' , dest = 'rm_comments' ,
167199 action = 'store_const' , const = True , default = False ,
168- help = "remove all comments (non-functional)" ,
200+ help = "Remove all comments (non-functional). " ,
169201)
170202group .add_argument (
171203 '--rm_blanks' , '-rb' , dest = 'rm_blanks' ,
172204 action = 'store_const' , const = True , default = False ,
173- help = "remove all empty lines (non-functional)" ,
205+ help = "Remove all empty lines (non-functional). " ,
174206)
175207group .add_argument (
176208 '--rm_whitespace' , '-rws' , dest = 'rm_whitespace' ,
177209 action = 'store_const' , const = True , default = False ,
178- help = "remove all whitespace from gcode blocks (non-functional)" ,
210+ help = "Remove all whitespace from gcode blocks (non-functional). " ,
179211)
180212group .add_argument (
181213 '--rm_gcodes' , '-rmg' , dest = 'rm_gcodes' ,
182214 type = word_list_type , default = [],
183- help = "remove gcode (and it's parameters) with words in the given list "
215+ help = "Remove gcode (and it's parameters) with words in the given list "
184216 "(eg: M6,G43) (note: only works for modal params with --full)" ,
185217)
186-
218+ group .add_argument (
219+ '--rm_invalid_modal' , '-rmim' , dest = 'rm_invalid_modal' ,
220+ action = 'store_const' , const = True , default = False ,
221+ help = "Simply remove everything that isn't understood. Use with caution." ,
222+ )
187223
188224# --- Parse Arguments
189225args = parser .parse_args ()
@@ -195,6 +231,7 @@ class MyMode(Mode):
195231
196232class MyMachine (Machine ):
197233 MODE_CLASS = MyMode
234+ ignore_invalid_modal = args .rm_invalid_modal
198235
199236machine = MyMachine ()
200237
@@ -276,6 +313,9 @@ def split_and_process(gcode_list, gcode_class, comment):
276313for line_str in args .infile .readlines ():
277314 line = Line (line_str )
278315
316+ if args .rm_invalid_modal :
317+ machine .clean_block (line .block )
318+
279319 # Effective G-Codes:
280320 # fills in missing motion modal gcodes (using machine's current motion mode).
281321 effective_gcodes = machine .block_modal_gcodes (line .block )
@@ -315,3 +355,23 @@ for line_str in args.infile.readlines():
315355 else :
316356 write (line .block .gcodes , modal_params = line .block .modal_params , comment = line .comment , macro = line .macro )
317357 machine .process_block (line .block )
358+
359+ # Finalizing Motion & Spindle
360+ if any ([args .spindle_off , args .zero_xy , args .zero_z ]):
361+ write ([], comment = Comment ("pygcode-norm: finalizing" ))
362+ if any ([args .zero_xy , args .zero_z ]) and not (isinstance (machine .mode .distance , GCodeAbsoluteDistanceMode )):
363+ write ([GCodeAbsoluteDistanceMode ()])
364+ if args .spindle_off :
365+ write ([GCodeStopSpindle ()], comment = Comment ("spindle off" ))
366+
367+ if args .zero_xy :
368+ rapid_safety_height = args .rapid_safety_height
369+ if rapid_safety_height is None :
370+ rapid_safety_height = machine .abs2work (machine .abs_range_max ).Z
371+
372+ if args .zero_xy :
373+ write ([GCodeRapidMove (Z = rapid_safety_height )], comment = Comment ("move to safe height" ))
374+ write ([GCodeRapidMove (X = 0 , Y = 0 )], comment = Comment ("move to planar origin" ))
375+
376+ if args .zero_z :
377+ write ([GCodeRapidMove (Z = 0 )], comment = Comment ("move to zero height" ))
0 commit comments