diff --git a/src/pygcode/dialects/__init__.py b/src/pygcode/dialects/__init__.py index 43edacb..847b881 100644 --- a/src/pygcode/dialects/__init__.py +++ b/src/pygcode/dialects/__init__.py @@ -9,6 +9,7 @@ __all__ = [ 'linuxcnc', 'reprap', 'prusa', + 'marlin2', ] @@ -20,9 +21,10 @@ from .mapping import word_dialect from . import linuxcnc from . import reprap from . import prusa +from . import marlin2 -_DEFAULT = 'prusa' +_DEFAULT = 'marlin2' def get_default(): @@ -53,5 +55,6 @@ def set_default(name): """ - # TODO: verify valid name + if name not in __all__: + raise ValueError(f"'{name}' is not a valid dialect") _DEFAULT = name diff --git a/src/pygcode/dialects/marlin2.py b/src/pygcode/dialects/marlin2.py new file mode 100644 index 0000000..e798446 --- /dev/null +++ b/src/pygcode/dialects/marlin2.py @@ -0,0 +1,206 @@ +import re + +from .utils import WordType + +# TODO get sall specific commands from https://github.com/MarlinFirmware/MarlinDocumentation/tree/master/_gcode ... +# and populate gcodes.py ; possibly move all dialect-related stuff here (including GCode* classes) + +# ======================== WORDS ======================== + +REGEX_FLOAT = re.compile(r'^\s*-?(\d+\.?\d*|\.\d+)') # testcase: ..tests.test_words.WordValueMatchTests.test_float +REGEX_INT = re.compile(r'^\s*-?\d+') +REGEX_POSITIVEINT = re.compile(r'^\s*\d+') +REGEX_CODE = re.compile(r'^\s*\d+(\.\d)?') # float, but can't be negative + +# Value cleaning functions +def _clean_codestr(value): + if value < 10: + return "0%g" % value + return "%g" % value + +CLEAN_NONE = lambda v: v +CLEAN_FLOAT = lambda v: "{0:g}".format(round(v, 3)) +CLEAN_CODE = _clean_codestr +CLEAN_INT = lambda v: "%g" % v + +WORD_MAP = { + # Descriptions copied from wikipedia: + # https://en.wikipedia.org/wiki/G-code#Letter_addresses + + # Rotational Axes + 'A': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Absolute or incremental position of A axis (rotational axis around X axis)", + clean_value=CLEAN_FLOAT, + ), + 'B': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Absolute or incremental position of B axis (rotational axis around Y axis)", + clean_value=CLEAN_FLOAT, + ), + 'C': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Absolute or incremental position of C axis (rotational axis around Z axis)", + clean_value=CLEAN_FLOAT, + ), + 'D': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Defines diameter or radial offset used for cutter compensation. D is used for depth of cut on lathes. It is used for aperture selection and commands on photoplotters.", + clean_value=CLEAN_FLOAT, + ), + 'E': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Extrusion value", + clean_value=CLEAN_FLOAT, + ), + # Feed Rates + 'F': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Feedrate", + clean_value=CLEAN_FLOAT, + ), + # G-Codes + 'G': WordType( + cls=float, + value_regex=REGEX_CODE, + description="Address for preparatory commands", + clean_value=CLEAN_CODE, + ), + # Tool Offsets + 'H': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Defines tool length offset; Incremental axis corresponding to C axis (e.g., on a turn-mill)", + clean_value=CLEAN_FLOAT, + ), + # Arc radius center coords + 'I': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Defines arc center in X axis for G02 or G03 arc commands. Also used as a parameter within some fixed cycles.", + clean_value=CLEAN_FLOAT, + ), + 'J': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Defines arc center in Y axis for G02 or G03 arc commands. Also used as a parameter within some fixed cycles.", + clean_value=CLEAN_FLOAT, + ), + 'K': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Defines arc center in Z axis for G02 or G03 arc commands. Also used as a parameter within some fixed cycles, equal to L address.", + clean_value=CLEAN_FLOAT, + ), + # Loop Count + 'L': WordType( + cls=int, + value_regex=REGEX_POSITIVEINT, + description="Fixed cycle loop count; Specification of what register to edit using G10", + clean_value=CLEAN_INT, + ), + # Miscellaneous Function + 'M': WordType( + cls=float, + value_regex=REGEX_CODE, + description="Miscellaneous function", + clean_value=CLEAN_CODE, + ), + # Line Number + 'N': WordType( + cls=int, + value_regex=REGEX_POSITIVEINT, + description="Line (block) number in program; System parameter number to change using G10", + clean_value=CLEAN_INT, + ), + # Program Name + 'O': WordType( + cls=str, + value_regex=re.compile(r'^.+$'), # all the way to the end + description="Program name", + clean_value=CLEAN_NONE, + ), + # Parameter (arbitrary parameter) + 'P': WordType( + cls=str, # parameter is often an integer, but can be a float + value_regex=re.compile(r'^.+$'), # all the way to the end + description="Checks the parameters of the printer and gcode and performs compatibility check", + clean_value=CLEAN_NONE, + ), + # Peck increment + 'Q': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Depth to increase on each peck; Peck increment in canned cycles", + clean_value=CLEAN_FLOAT, + ), + # Arc Radius + 'R': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Defines size of arc radius, or defines retract height in milling canned cycles", + clean_value=CLEAN_FLOAT, + ), + # Spindle speed + 'S': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Defines speed, either spindle speed or surface speed depending on mode", + clean_value=CLEAN_FLOAT, + ), + # Tool Selecton + 'T': WordType( + cls=str, + value_regex=REGEX_POSITIVEINT, # tool string may have leading '0's, but is effectively an index (integer) + description="Tool selection", + clean_value=CLEAN_NONE, + ), + # Incremental axes + 'U': WordType( + cls=str, + value_regex=re.compile(r'^.*$'), # all the way to the end + description="todo", + clean_value=CLEAN_NONE, + ), + 'V': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Incremental axis corresponding to Y axis", + clean_value=CLEAN_FLOAT, + ), + 'W': WordType( + cls=str, + value_regex=re.compile(r'^.*$'), # all the way to the end + description="When used with G28 just specifies without bed leveling, when used with M900 specifies width", + clean_value=CLEAN_NONE, + ), + # Linear Axes + 'X': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Absolute or incremental position of X axis.", + clean_value=CLEAN_FLOAT, + ), + 'Y': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Absolute or incremental position of Y axis.", + clean_value=CLEAN_FLOAT, + ), + 'Z': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Absolute or incremental position of Z axis.", + clean_value=CLEAN_FLOAT, + ), +} + + +# ======================== G-CODES ======================== +# cannot `from ..gcodes import GCode` because or circular importor circular import!! diff --git a/src/pygcode/gcodes.py b/src/pygcode/gcodes.py index f396494..3e5f02a 100644 --- a/src/pygcode/gcodes.py +++ b/src/pygcode/gcodes.py @@ -73,6 +73,11 @@ from .exceptions import GCodeParameterError, GCodeWordStrError # User Defined (Group 10) M100-M199 # +DIALECT_UNKNOWN = [False] +# TODO see https://github.com/petaflot/pygcode/issues/6 for a more complete +from pygcode.dialects import get_default as get_default_dialect +import pygcode + MODAL_GROUP_MAP = { # "G" codes 'motion': 1, @@ -139,1343 +144,13 @@ MODAL_GROUP_MAP = { # 240: Perform motion (G0 to G3, G33, G38.x, G73, G76, G80 to G89), as modified (possibly) by G53. # 250: Stop (M0, M1, M2, M30, M60). - -class GCode(object): - # Defining Word - word_key = None # Word instance to use in lookup - word_matches = None # function (secondary) - default_word = None - word_letter = 'G' - word_value_configurable = False # if set, word value can be the first parameter - - # Parameters associated to this gcode - param_letters = set() - - # Modal stuff - modal_group = None - modal_param_letters = set() # by default: no parameters are retained in modal state - - # Execution Order - exec_order = 999 # if not otherwise specified, run last - - def __init__(self, *words, **params): - """ - :param word: Word instance defining gcode (eg: Word('G0') for rapid movement) - :param params: list of Word instances (eg: Word('X-1.2') as x-coordinate) - """ - gcode_word_list = words[:1] - param_words = words[1:] - if gcode_word_list: - gcode_word = gcode_word_list[0] - if self.word_value_configurable and isinstance(gcode_word, six.integer_types + (float,)): - gcode_word = Word(self.word_letter, gcode_word) # cast to Word() - else: - gcode_word = self._default_word() - assert isinstance(gcode_word, Word), "invalid gcode word %r" % gcode_word - self.word = gcode_word - self.params = {} - - # Whitespace as prefix - # if True, str(self) will repalce self.word code with whitespace - self._whitespace_prefix = False - - # Add Given Parameters - for param_word in param_words: - self.add_parameter(param_word) - for (k, v) in params.items(): - self.add_parameter(Word(k, v)) - - def __repr__(self): - param_str = '' - if self.params: - param_str = "{%s}" % (', '.join([ - "{}".format(self.params[k]) - for k in sorted(self.params.keys()) - ])) - return "<{class_name}: {gcode}{params}>".format( - class_name=self.__class__.__name__, - gcode=self.word, - params=param_str, - ) - - def __str__(self): - """String representation of gcode, as it would be seen in a .gcode file""" - param_str = '' - if self.params: - param_str += ' ' + ' '.join([ - "{}".format(self.params[k]) - for k in sorted(self.params.keys()) - ]) - word_str = str(self.word) - if self._whitespace_prefix: - word_str = ' ' * len(word_str) - return "{word_str}{parameters}".format( - word_str=word_str, - parameters=param_str, - ) - - def __hash__(self): - """Hash representation of the gcode, for set and dictionary usage""" - try: - return hash(self.word_key) - except TypeError: - return hash(self.word_letter) # May also want to retrieve additional value info - - - def _default_word(self): - if self.default_word: - return copy(self.default_word) - elif self.word_key: - return copy(self.word_key) - raise AssertionError("class %r has no default word" % self.__class__) - - # Equality - def __eq__(self, other): - return ( - (self.word == other.word) and - (self.params == other.params) - ) - - def __ne__(self, other): - return not self.__eq__(other) - - # Sort by execution order - def __lt__(self, other): - return self.exec_order < other.exec_order - - def __le__(self, other): - return self.exec_order <= other.exec_order - - def __gt__(self, other): - return self.exec_order > other.exec_order - - def __ge__(self, other): - return self.exec_order >= other.exec_order - - # Parameters - def add_parameter(self, word): - """ - Add given word as a parameter for this gcode - :param word: Word instance - """ - assert isinstance(word, Word), "invalid parameter class: %r" % word - if word.letter not in self.param_letters: - raise GCodeParameterError("invalid parameter for %s: %s" % (self.__class__.__name__, str(word))) - if word.letter in self.params: - raise GCodeParameterError("parameter defined twice: %s -> %s" % (self.params[word.letter], word)) - - self.params[word.letter] = word - - # Assert Parameters - def assert_params(self): - """ - Assert validity of gcode's parameters. - This verification is irrespective of machine, or machine's state; - verification is g-code language-based verification - :raises: GCodeParameterError - """ - # to be overridden in inheriting classes - pass - - def __getattr__(self, key): - # Return parameter values (if valid parameter for gcode) - if key in self.param_letters: - if key in self.params: - return self.params[key].value - else: - return None # parameter is valid for GCode, but undefined - - raise AttributeError("'{cls}' object has no attribute '{key}'".format( - cls=self.__class__.__name__, - key=key - )) - - def __setattr__(self, key, value): - if key in self.param_letters: - if key in self.params: - self.params[key].value = value - else: - self.add_parameter(Word(key, value)) - - else: - self.__dict__[key] = value - - @property - def description(self): - return self.__doc__ - - def modal_copy(self): - """Copy of GCode instance containing only parameters listed in modal_param_letters""" - return self.__class__(self.word, *[ - w for (l, w) in self.params.items() - if l in self.modal_param_letters - ]) - - def get_param_dict(self, letters=None, lc=False): - """ - Get gcode parameters as a dict - gcode parameter like "X3.1, Y-2" would return {'X': 3.1, 'Y': -2} - :param letters: iterable whitelist of letters to include as dict keys - :param lc: lower case parameter letters - :return: dict of gcode parameters' (letter, value) pairs - """ - letter_mod = lambda x: x - if lc: - letter_mod = lambda x: x.lower() - return dict( - (letter_mod(w.letter), w.value) for w in self.params.values() - if (letters is None) or (w.letter in letters) - ) - - # Process GCode - def process(self, machine): - """ - Process a GCode on the given Machine - :param machine: Machine instance, to change state - :return: GCodeEffect instance; effect the gcode just had on machine - """ - from .machine import Machine # importing up (done live to avoid dependency loop) - assert isinstance(machine, Machine), "invalid machine type: %r" % machine - - # Set mode - self._process_mode(machine) - - # GCode specific - self._process(machine) - - def _process_mode(self, machine): - """Set machine's state""" - machine.set_mode(self) - - def _process(self, machine): - """Process this GCode (to be overridden)""" - pass - - -# ======================= Non Operational ======================= -# CODE PARAMETERS DESCRIPTION -# N# Define line number (oldschool) -# O Define program name - -class GCodeDefinition(GCode): - pass - - -class GCodeLineNumber(GCodeDefinition): - """N: Line Number""" - word_letter = 'N' - word_value_configurable = True - exec_order = 0 - - @classmethod - def word_matches(cls, w): - return w.letter == 'N' - - @property - def number(self): - return self.word.value - - -class GCodeProgramName(GCodeDefinition): - """O: Program Name""" - word_letter = 'O' - word_value_configurable = True - exec_order = 1 - - @classmethod - def word_matches(cls, w): - return w.letter == 'O' - - @property - def name(self): - return self.word.value - - -# ======================= Motion ======================= -# (X Y Z A B C U V W apply to all motions) -# CODE PARAMETERS DESCRIPTION -# G0 Rapid Move -# G1 Linear Move -# G2, G3 I J K or R, P Arc Move -# G4 P Dwell -# G5 I J P Q Cubic Spline -# G5.1 I J Quadratic Spline -# G5.2 P L NURBS -# G38.2 - G38.5 Straight Probe -# G33 K Spindle Synchronized Motion -# G33.1 K Rigid Tapping - -class GCodeMotion(GCode): - param_letters = set('XYZABCUVW') - modal_group = MODAL_GROUP_MAP['motion'] - exec_order = 242 - - def _process(self, machine): - machine.move_to(**self.get_param_dict(letters=machine.axes)) - - -class GCodeRapidMove(GCodeMotion): - """G0: Rapid Move""" - word_key = Word('G', 0) - - def _process(self, machine): - machine.move_to(rapid=True, **self.get_param_dict(letters=machine.axes)) - - -class GCodeLinearMove(GCodeMotion): - """G1: Linear Move""" - word_key = Word('G', 1) - - -class GCodeArcMove(GCodeMotion): - """Arc Move""" - param_letters = GCodeMotion.param_letters | set('IJKRP') - - def assert_params(self): - param_letters = set(self.params.keys()) - # Parameter groups - params_xyz = set('XYZ') & set(param_letters) - params_ijk = set('IJK') & set(param_letters) - params_r = set('R') & set(param_letters) - params_ijkr = params_ijk | params_r - - # --- Parameter Groups - # XYZ: at least 1 - if not params_xyz: - raise GCodeParameterError("no XYZ parameters set for destination: %r" % arc_gcode) - # IJK or R: only in 1 group - if params_ijk and params_r: - raise GCodeParameterError("both IJK and R parameters defined: %r" % arc_gcode) - # IJKR: at least 1 - if not params_ijkr: - raise GCodeParameterError("neither IJK or R parameters defined: %r" % arc_gcode) - - # --- Parameter Values - if params_r and (self.R == 0): - raise GCodeParameterError("cannot plot a circle with a radius of zero: %r" % arc_gcode) - - -class GCodeArcMoveCW(GCodeArcMove): - """G2: Arc Move (clockwise)""" - word_key = Word('G', 2) - - -class GCodeArcMoveCCW(GCodeArcMove): - """G3: Arc Move (counter-clockwise)""" - word_key = Word('G', 3) - - -class GCodeDwell(GCodeMotion): - """G4: Dwell""" - param_letters = set('P') # doesn't accept axis parameters - word_key = Word('G', 4) - modal_group = None # one of the few motion commands that isn't modal - exec_order = 140 - - def _process(self, machine): - pass # no movements made - - -class GCodeCublcSpline(GCodeMotion): - """G5: Cubic Spline""" - param_letters = GCodeMotion.param_letters | set('IJPQ') - word_key = Word('G', 5) - - -class GCodeQuadraticSpline(GCodeMotion): - """G5.1: Quadratic Spline""" - param_letters = GCodeMotion.param_letters | set('IJ') - word_key = Word('G', 5.1) - - -class GCodeNURBS(GCodeMotion): - """G5.2: Non-uniform rational basis spline (NURBS)""" - param_letters = GCodeMotion.param_letters | set('PL') - word_key = Word('G', 5.2) - - -class GCodeNURBSEnd(GCodeNURBS): - """G5.3: end NURBS mode""" - word_key = Word('G', 5.3) - - -class GCodeStraightProbe(GCodeMotion): - """G38.2-G38.5: Straight Probe""" - @classmethod - def word_matches(cls, w): - return (w.letter == 'G') and (38.2 <= w.value <= 38.5) - default_word = Word('G', 38.2) - - -class GCodeSpindleSyncMotion(GCodeMotion): - """G33: Spindle Synchronized Motion""" - param_letters = GCodeMotion.param_letters | set('K') - word_key = Word('G', 33) - - -class GCodeRigidTapping(GCodeMotion): - """G33.1: Rigid Tapping""" - param_letters = GCodeMotion.param_letters | set('K') - word_key = Word('G', 33.1) - - -# ======================= Canned Cycles ======================= -# (X Y Z or U V W apply to canned cycles, depending on active plane) -# CODE PARAMETERS DESCRIPTION -# G81 R L (P) Drilling Cycle -# G82 R L (P) Drilling Cycle, Dwell -# G83 R L Q Drilling Cycle, Peck -# G73 R L Q Drilling Cycle, Chip Breaking -# G85 R L (P) Boring Cycle, Feed Out -# G89 R L (P) Boring Cycle, Dwell, Feed Out -# G76 P Z I J R K Q H L E Threading Cycle -# G80 Cancel Canned Cycle - -class GCodeCannedCycle(GCode): - param_letters = set('XYZUVW') - modal_group = MODAL_GROUP_MAP['canned_cycle'] - exec_order = 242 - - def _process(self, machine): - moveto_coords = self.get_param_dict(letters=machine.axes) - if isinstance(machine.mode.canned_cycles_return, GCodeCannedCycleReturnToR): - # canned return is to this.R, not this.Z (plane dependent) - moveto_coords.update({ - machine.mode.plane_selection.normal_axis: self.R, - }) - else: # default: GCodeCannedCycleReturnPrevLevel - # Remove this.Z (plane dependent) value (ie: no machine movement on this axis) - moveto_coords.pop(machine.mode.plane_selection.normal_axis, None) - - # Process action 'L' times - if hasattr(self, 'L'): - loop_count = self.L - if (loop_count is None) or (loop_count <= 0): - loop_count = 1 - for i in range(loop_count): - machine.move_to(**moveto_coords) - - -class GCodeDrillingCycle(GCodeCannedCycle): - """G81: Drilling Cycle""" - param_letters = GCodeCannedCycle.param_letters | set('RLP') - word_key = Word('G', 81) - modal_param_letters = GCodeCannedCycle.param_letters | set('RP') - - -class GCodeDrillingCycleDwell(GCodeCannedCycle): - """G82: Drilling Cycle, Dwell""" - param_letters = GCodeCannedCycle.param_letters | set('RLP') - word_key = Word('G', 82) - modal_param_letters = GCodeCannedCycle.param_letters | set('RP') - - -class GCodeDrillingCyclePeck(GCodeCannedCycle): - """G83: Drilling Cycle, Peck""" - param_letters = GCodeCannedCycle.param_letters | set('RLQ') | set('IJK') - word_key = Word('G', 83) - modal_param_letters = GCodeCannedCycle.param_letters | set('RQ') - - -class GCodeDrillingCycleChipBreaking(GCodeCannedCycle): - """G73: Drilling Cycle, ChipBreaking""" - param_letters = GCodeCannedCycle.param_letters | set('RLQ') - word_key = Word('G', 73) - modal_param_letters = GCodeCannedCycle.param_letters | set('RQ') - - -class GCodeBoringCycleFeedOut(GCodeCannedCycle): - """G85: Boring Cycle, Feed Out""" - param_letters = GCodeCannedCycle.param_letters | set('RLP') - word_key = Word('G', 85) - modal_param_letters = GCodeCannedCycle.param_letters | set('RP') - - -class GCodeBoringCycleDwellFeedOut(GCodeCannedCycle): - """G89: Boring Cycle, Dwell, Feed Out""" - param_letters = GCodeCannedCycle.param_letters | set('RLP') - word_key = Word('G', 89) - modal_param_letters = GCodeCannedCycle.param_letters | set('RP') - - -class GCodeThreadingCycle(GCodeCannedCycle): - """G76: Threading Cycle""" - param_letters = GCodeCannedCycle.param_letters | set('PZIJRKQHLE') - word_key = Word('G', 76) - -class GCodeCancelCannedCycle(GCodeCannedCycle): - """ G80: Cancel Canned Cycle """ - param_letters = set() - word_key = Word('G', 80) - # Modal Group - # Technically G80 belongs to the motion modal group, however it's often - # expressed in the same line as another motion command. - # This is alowed, but executed just prior to any other motion command - # eg: G00 G80 - # will leave the machine in rapid motion mode - # Just running G80 will leave machine with no motion mode. - exec_order = 241 - - -# ======================= Distance Mode ======================= -# CODE PARAMETERS DESCRIPTION -# G90, G91 Distance Mode -# G90.1, G91.1 Arc Distance Mode -# G7 Lathe Diameter Mode -# G8 Lathe Radius Mode - -class GCodeDistanceMode(GCode): - exec_order = 210 - - -class GCodeAbsoluteDistanceMode(GCodeDistanceMode): - """G90: Absolute Distance Mode""" - word_key = Word('G', 90) - modal_group = MODAL_GROUP_MAP['distance'] - - -class GCodeIncrementalDistanceMode(GCodeDistanceMode): - """G91: Incremental Distance Mode""" - word_key = Word('G', 91) - modal_group = MODAL_GROUP_MAP['distance'] - - -class GCodeAbsoluteArcDistanceMode(GCodeDistanceMode): - """G90.1: Absolute Distance Mode for Arc IJK Parameters""" - word_key = Word('G', 90.1) - modal_group = MODAL_GROUP_MAP['arc_ijk_distance'] - - -class GCodeIncrementalArcDistanceMode(GCodeDistanceMode): - """G91.1: Incremental Distance Mode for Arc IJK Parameters""" - word_key = Word('G', 91.1) - modal_group = MODAL_GROUP_MAP['arc_ijk_distance'] - - -class GCodeLatheDiameterMode(GCodeDistanceMode): - """G7: Lathe Diameter Mode""" - word_key = Word('G', 7) - modal_group = MODAL_GROUP_MAP['lathe_diameter'] - - -class GCodeLatheRadiusMode(GCodeDistanceMode): - """G8: Lathe Radius Mode""" - word_key = Word('G', 8) - modal_group = MODAL_GROUP_MAP['lathe_diameter'] - - -# ======================= Feed Rate Mode ======================= -# CODE PARAMETERS DESCRIPTION -# G93, G94, G95 Feed Rate Mode - -class GCodeFeedRateMode(GCode): - modal_group = MODAL_GROUP_MAP['feed_rate_mode'] - exec_order = 30 - - -class GCodeInverseTimeMode(GCodeFeedRateMode): - """G93: Inverse Time Mode""" - word_key = Word('G', 93) - - -class GCodeUnitsPerMinuteMode(GCodeFeedRateMode): - """G94: Units Per MinuteMode""" - word_key = Word('G', 94) - - -class GCodeUnitsPerRevolution(GCodeFeedRateMode): - """G95: Units Per Revolution""" - word_key = Word('G', 95) - - -# ======================= Spindle Control ======================= -# CODE PARAMETERS DESCRIPTION -# M3, M4, M5 S Spindle Control -# M19 Orient Spindle -# G96, G97 S D Spindle Control Mode - -class GCodeSpindle(GCode): - word_letter = 'M' - exec_order = 90 - - -class GCodeStartSpindle(GCodeSpindle): - """M3,M4: Start Spindle Clockwise""" - modal_group = MODAL_GROUP_MAP['spindle'] - - -class GCodeStartSpindleCW(GCodeStartSpindle): - """M3: Start Spindle Clockwise""" - #param_letters = set('S') # S is it's own gcode, makes no sense to be here - word_key = Word('M', 3) - -class GCodeStartSpindleCCW(GCodeStartSpindle): - """M4: Start Spindle Counter-Clockwise""" - #param_letters = set('S') # S is it's own gcode, makes no sense to be here - word_key = Word('M', 4) - - -class GCodeStopSpindle(GCodeSpindle): - """M5: Stop Spindle""" - #param_letters = set('S') # S is it's own gcode, makes no sense to be here - word_key = Word('M', 5) - modal_group = MODAL_GROUP_MAP['spindle'] - - -class GCodeOrientSpindle(GCodeSpindle): - """M19: Orient Spindle""" - word_key = Word('M', 19) - - -class GCodeSpindleSpeedMode(GCodeSpindle): - word_letter = 'G' - modal_group = MODAL_GROUP_MAP['spindle_speed_mode'] - - -class GCodeSpindleConstantSurfaceSpeedMode(GCodeSpindleSpeedMode): - """G96: Spindle Constant Surface Speed""" - param_letters = set('DS') - word_key = Word('G', 96) - - -class GCodeSpindleRPMMode(GCodeSpindleSpeedMode): - """G97: Spindle RPM Speed""" - param_letters = set('D') - word_key = Word('G', 97) - - - -# ======================= Coolant ======================= -# CODE PARAMETERS DESCRIPTION -# M7, M8, M9 Coolant Control - -class GCodeCoolant(GCode): - word_letter = 'M' - modal_group = MODAL_GROUP_MAP['coolant'] - exec_order = 110 - - -class GCodeCoolantMistOn(GCodeCoolant): - """M7: turn mist coolant on""" - word_key = Word('M', 7) - - -class GCodeCoolantFloodOn(GCodeCoolant): - """M8: turn flood coolant on""" - word_key = Word('M', 8) - - -class GCodeCoolantOff(GCodeCoolant): - """M9: turn all coolant off""" - word_key = Word('M', 9) - - -# ======================= Tool Length ======================= -# CODE PARAMETERS DESCRIPTION -# G43 H Tool Length Offset -# G43.1 Dynamic Tool Length Offset -# G43.2 H Apply additional Tool Length Offset -# G49 Cancel Tool Length Compensation - -class GCodeToolLength(GCode): - modal_group = MODAL_GROUP_MAP['tool_length_offset'] - exec_order = 180 - - -class GCodeToolLengthOffset(GCodeToolLength): - """G43: Tool Length Offset""" - param_letters = set('H') - word_key = Word('G', 43) - - -class GCodeDynamicToolLengthOffset(GCodeToolLength): - """G43.1: Dynamic Tool Length Offset""" - word_key = Word('G', 43.1) - - -class GCodeAddToolLengthOffset(GCodeToolLength): - """G43.2: Appkly Additional Tool Length Offset""" - param_letters = set('H') - word_key = Word('G', 43.2) - - -class GCodeCancelToolLengthOffset(GCodeToolLength): - """G49: Cancel Tool Length Compensation""" - word_key = Word('G', 49) - - -# ======================= Stopping (Program Control) ======================= -# CODE PARAMETERS DESCRIPTION -# M0, M1 Program Pause -# M2, M30 Program End -# M60 Pallet Change Pause - -class GCodeProgramControl(GCode): - word_letter = 'M' - modal_group = MODAL_GROUP_MAP['stopping'] - exec_order = 250 - -class GCodePauseProgram(GCodeProgramControl): - """M0: Program Pause""" - word_key = Word('M', 0) - - -class GCodePauseProgramOptional(GCodeProgramControl): - """M1: Program Pause (optional)""" - word_key = Word('M', 1) - - -class GCodeEndProgram(GCodeProgramControl): - """M2: Program End""" - word_key = Word('M', 2) - - -class GCodeEndProgramPalletShuttle(GCodeProgramControl): - """M30: exchange pallet shuttles and end the program""" - word_key = Word('M', 30) - - -class GCodePalletChangePause(GCodeProgramControl): - """M60: Pallet Change Pause""" - word_key = Word('M', 60) - - -# ======================= Units ======================= -# CODE PARAMETERS DESCRIPTION -# G20, G21 Units - -class GCodeUnit(GCode): - modal_group = MODAL_GROUP_MAP['units'] - exec_order = 160 - - -class GCodeUseInches(GCodeUnit): - """G20: use inches for length units""" - word_key = Word('G', 20) - unit_id = 0 - - -class GCodeUseMillimeters(GCodeUnit): - """G21: use millimeters for length units""" - word_key = Word('G', 21) - unit_id = 1 - - -# ======================= Plane Selection ======================= -# (affects G2, G3, G81-G89, G40-G42) -# CODE PARAMETERS DESCRIPTION -# G17 - G19.1 Plane Select - -class GCodePlaneSelect(GCode): - modal_group = MODAL_GROUP_MAP['plane_selection'] - exec_order = 150 - - # -- Plane Orientation Quaternion - # Such that... - # vectorXY = Vector3() - # vectorZX = GCodeSelectZXPlane.quat * vectorXY - # vectorZX += some_offset_vector - # vectorXY = GCodeSelectZXPlane.quat.conjugate() * vectorZX - # note: all quaternions use the XY plane as a basis - # To transform from ZX to YZ planes via these quaternions, you must - # first translate it to XY, like so: - # vectorYZ = GCodeSelectYZPlane.quat * (GCodeSelectZXPlane.quat.conjugate() * vectorZX) - quat = None # Quaternion - - # -- Plane Axis Information - # Vector normal to plane (such that XYZ axes follow the right-hand rule) - normal_axis = None # Letter of normal axis (upper case) - # Axes of plane - plane_axes = set() - normal = None # Vector3 - - -class GCodeSelectXYPlane(GCodePlaneSelect): - """G17: select XY plane (default)""" - word_key = Word('G', 17) - quat = Quaternion() # no effect - normal_axis = 'Z' - plane_axes = set('XY') - normal = Vector3(0., 0., 1.) - - -class GCodeSelectZXPlane(GCodePlaneSelect): - """G18: select ZX plane""" - word_key = Word('G', 18) - quat = quat2coord_system( - Vector3(1., 0., 0.), Vector3(0., 1., 0.), - Vector3(0., 0., 1.), Vector3(1., 0., 0.) - ) - normal_axis = 'Y' - plane_axes = set('ZX') - normal = Vector3(0., 1., 0.) - - -class GCodeSelectYZPlane(GCodePlaneSelect): - """G19: select YZ plane""" - word_key = Word('G', 19) - quat = quat2coord_system( - Vector3(1., 0., 0.), Vector3(0., 1., 0.), - Vector3(0., 1., 0.), Vector3(0., 0., 1.) - ) - normal_axis = 'X' - plane_axes = set('YZ') - normal = Vector3(1., 0., 0.) - - -class GCodeSelectUVPlane(GCodePlaneSelect): - """G17.1: select UV plane""" - word_key = Word('G', 17.1) - - -class GCodeSelectWUPlane(GCodePlaneSelect): - """G18.1: select WU plane""" - word_key = Word('G', 18.1) - - -class GCodeSelectVWPlane(GCodePlaneSelect): - """G19.1: select VW plane""" - word_key = Word('G', 19.1) - - -# ======================= Cutter Radius Compensation ======================= -# CODE PARAMETERS DESCRIPTION -# G40 Compensation Off -# G41,G42 D Cutter Compensation -# G41.1, G42.1 D L Dynamic Cutter Compensation - -class GCodeCutterRadiusComp(GCode): - modal_group = MODAL_GROUP_MAP['cutter_diameter_comp'] - exec_order = 170 - - -class GCodeCutterRadiusCompOff(GCodeCutterRadiusComp): - """G40: Cutter Radius Compensation Off""" - word_key = Word('G', 40) - - -class GCodeCutterCompLeft(GCodeCutterRadiusComp): - """G41: Cutter Radius Compensation (left)""" - param_letters = set('D') - word_key = Word('G', 41) - - -class GCodeCutterCompRight(GCodeCutterRadiusComp): - """G42: Cutter Radius Compensation (right)""" - param_letters = set('D') - word_key = Word('G', 42) - - -class GCodeDynamicCutterCompLeft(GCodeCutterRadiusComp): - """G41.1: Dynamic Cutter Radius Compensation (left)""" - param_letters = set('DL') - word_key = Word('G', 41.1) - - -class GCodeDynamicCutterCompRight(GCodeCutterRadiusComp): - """G42.1: Dynamic Cutter Radius Compensation (right)""" - param_letters = set('DL') - word_key = Word('G', 42.1) - - -# ======================= Path Control Mode ======================= -# CODE PARAMETERS DESCRIPTION -# G61 G61.1 Exact Path Mode -# G64 P Q Path Blending - -class GCodePathControlMode(GCode): - modal_group = MODAL_GROUP_MAP['control_mode'] - exec_order = 200 - - -class GCodeExactPathMode(GCodePathControlMode): - """G61: Exact path mode""" - word_key = Word('G', 61) - - -class GCodeExactStopMode(GCodePathControlMode): - """G61.1: Exact stop mode""" - word_key = Word('G', 61.1) - - -class GCodePathBlendingMode(GCodePathControlMode): - """G64: Path Blending""" - param_letters = set('PQ') - word_key = Word('G', 64) - - -# ======================= Return Mode in Canned Cycles ======================= -# CODE PARAMETERS DESCRIPTION -# G98 Canned Cycle Return Level to previous -# G99 Canned Cycle Return to the level set by R - -class GCodeCannedReturnMode(GCode): - modal_group = MODAL_GROUP_MAP['canned_cycles_return'] - exec_order = 220 - - -class GCodeCannedCycleReturnPrevLevel(GCodeCannedReturnMode): - """G98: Canned Cycle Return to the level set prior to cycle start""" - # "retract to the position that axis was in just before this series of one or more contiguous canned cycles was started" - word_key = Word('G', 98) - - -class GCodeCannedCycleReturnToR(GCodeCannedReturnMode): - """G99: Canned Cycle Return to the level set by R""" - # "retract to the position specified by the R word of the canned cycle" - word_key = Word('G', 99) - - -# ======================= Other Modal Codes ======================= -# CODE PARAMETERS DESCRIPTION -# F Set Feed Rate -# S Set Spindle Speed -# T Select Tool -# M48, M49 Speed and Feed Override Control -# M50 P0 (off) or P1 (on) Feed Override Control -# M51 P0 (off) or P1 (on) Spindle Speed Override Control -# M52 P0 (off) or P1 (on) Adaptive Feed Control -# M53 P0 (off) or P1 (on) Feed Stop Control -# G54-G59.3 Select Coordinate System - -class GCodeOtherModal(GCode): - pass - - -class GCodeFeedRate(GCodeOtherModal): - """F: Set Feed Rate""" - word_letter = 'F' - word_value_configurable = True - @classmethod - def word_matches(cls, w): - return w.letter == 'F' - default_word = Word('F', 0) - modal_group = MODAL_GROUP_MAP['feed_rate'] - exec_order = 40 - - -class GCodeSpindleSpeed(GCodeOtherModal): - """S: Set Spindle Speed""" - word_letter = 'S' - word_value_configurable = True - @classmethod - def word_matches(cls, w): - return w.letter == 'S' - default_word = Word('S', 0) - # Modal Group: (see description in GCodeFeedRate) - modal_group = MODAL_GROUP_MAP['spindle_speed'] - exec_order = 50 - - -class GCodeSelectTool(GCodeOtherModal): - """T: Select Tool""" - word_letter = 'T' - word_value_configurable = True - @classmethod - def word_matches(cls, w): - return w.letter == 'T' - default_word = Word('T', 0) - # Modal Group: (see description in GCodeFeedRate) - modal_group = MODAL_GROUP_MAP['tool'] - exec_order = 60 - - -class GCodeSpeedAndFeedOverrideOn(GCodeOtherModal): - """M48: Speed and Feed Override Control On""" - word_letter = 'M' - word_key = Word('M', 48) - modal_group = MODAL_GROUP_MAP['override_switches'] - exec_order = 120 - - -class GCodeSpeedAndFeedOverrideOff(GCodeOtherModal): - """M49: Speed and Feed Override Control Off""" - word_letter = 'M' - word_key = Word('M', 49) - modal_group = MODAL_GROUP_MAP['override_switches'] - exec_order = 120 - - -class GCodeFeedOverride(GCodeOtherModal): - """M50: Feed Override Control""" - word_letter = 'M' - param_letters = set('P') - word_key = Word('M', 50) - exec_order = 120 - - -class GCodeSpindleSpeedOverride(GCodeOtherModal): - """M51: Spindle Speed Override Control""" - word_letter = 'M' - param_letters = set('P') - word_key = Word('M', 51) - exec_order = 120 - - -class GCodeAdaptiveFeed(GCodeOtherModal): - """M52: Adaptive Feed Control""" - word_letter = 'M' - param_letters = set('P') - word_key = Word('M', 52) - exec_order = 120 - - -class GCodeFeedStop(GCodeOtherModal): - """M53: Feed Stop Control""" - word_letter = 'M' - param_letters = set('P') - word_key = Word('M', 53) - exec_order = 120 - - -class GCodeSelectCoordinateSystem(GCodeOtherModal): - """Select Coordinate System""" - modal_group = MODAL_GROUP_MAP['coordinate_system'] - exec_order = 190 - coord_system_id = None # overridden in inheriting classes - - -class GCodeSelectCoordinateSystem1(GCodeSelectCoordinateSystem): - """Select Coordinate System 1""" - word_key = Word('G', 54) - coord_system_id = 1 - - -class GCodeSelectCoordinateSystem2(GCodeSelectCoordinateSystem): - """Select Coordinate System 2""" - word_key = Word('G', 55) - coord_system_id = 2 - - -class GCodeSelectCoordinateSystem3(GCodeSelectCoordinateSystem): - """Select Coordinate System 3""" - word_key = Word('G', 56) - coord_system_id = 3 - - -class GCodeSelectCoordinateSystem4(GCodeSelectCoordinateSystem): - """Select Coordinate System 4""" - word_key = Word('G', 57) - coord_system_id = 4 - - -class GCodeSelectCoordinateSystem5(GCodeSelectCoordinateSystem): - """Select Coordinate System 5""" - word_key = Word('G', 58) - coord_system_id = 5 - - -class GCodeSelectCoordinateSystem6(GCodeSelectCoordinateSystem): - """Select Coordinate System 6""" - word_key = Word('G', 59) - coord_system_id = 6 - - -class GCodeSelectCoordinateSystem7(GCodeSelectCoordinateSystem): - """Select Coordinate System 7""" - coord_system_id = 7 - word_key = Word('G', 59.1) - - -class GCodeSelectCoordinateSystem8(GCodeSelectCoordinateSystem): - """Select Coordinate System 8""" - word_key = Word('G', 59.2) - coord_system_id = 8 - - -class GCodeSelectCoordinateSystem9(GCodeSelectCoordinateSystem): - """Select Coordinate System 9""" - word_key = Word('G', 59.3) - coord_system_id = 9 - - -# ======================= Flow-control Codes ======================= -# CODE PARAMETERS DESCRIPTION -# o sub Subroutines, sub/endsub call [unsupported] -# o while Looping, while/endwhile do/while [unsupported] -# o if Conditional, if/else/endif [unsupported] -# o repeat Repeat a loop of code [unsupported] -# [] Indirection [unsupported] -# o call Call named file [unsupported] -# M70 Save modal state [unsupported] -# M71 Invalidate stored state [unsupported] -# M72 Restore modal state [unsupported] -# M73 Save and Auto-restore modal state [unsupported] - - -# ======================= Input/Output Codes ======================= -# CODE PARAMETERS DESCRIPTION -# M62 - M65 P Digital Output Control -# M66 P E L Q Wait on Input -# M67 T Analog Output, Synchronized -# M68 T Analog Output, Immediate - -class GCodeIO(GCode): - word_letter = 'M' - exec_order = 70 - - -class GCodeDigitalOutput(GCodeIO): - """Digital Output Control""" - param_letters = set('P') - - -class GCodeDigitalOutputOnSyncd(GCodeDigitalOutput): - """M62: turn on digital output synchronized with motion""" - word_key = Word('M', 62) - - -class GCodeDigitalOutputOffSyncd(GCodeDigitalOutput): - """M63: turn off digital output synchronized with motion""" - word_key = Word('M', 63) - - -class GCodeDigitalOutputOn(GCodeDigitalOutput): - """M64: turn on digital output immediately""" - word_key = Word('M', 64) - - -class GCodeDigitalOutputOff(GCodeDigitalOutput): - """M65: turn off digital output immediately""" - word_key = Word('M', 65) - - -class GCodeWaitOnInput(GCodeIO): - """M66: Wait on Input""" - param_letters = set('PELQ') - word_key = Word('M', 66) - - -class GCodeAnalogOutput(GCodeIO): - """Analog Output""" - param_letters = set('T') - - -class GCodeAnalogOutputSyncd(GCodeAnalogOutput): - """M67: Analog Output, Synchronized""" - word_key = Word('M', 67) - - -class GCodeAnalogOutputImmediate(GCodeAnalogOutput): - """M68: Analog Output, Immediate""" - word_key = Word('M', 68) - - -# ======================= Non-modal Codes ======================= -# CODE PARAMETERS DESCRIPTION -# M6 T Tool Change -# M61 Q Set Current Tool -# G10 L1 P Q R Set Tool Table -# G10 L10 P Set Tool Table -# G10 L11 P Set Tool Table -# G10 L2 P R ABCXYZ Set Coordinate System -# G10 L20 P ABCXYZ Set Coordinate System -# G28, G28.1 Go/Set Predefined Position -# G30, G30.1 Go/Set Predefined Position -# G53 Move in Machine Coordinates -# G92 ABCXYZUVW Coordinate System Offset -# G92.1, G92.2 Reset G92 Offsets -# G92.3 Restore G92 Offsets -# M101 - M199 P Q User Defined Commands - -class GCodeNonModal(GCode): - pass - - -class GCodeToolChange(GCodeNonModal): - """M6: Tool Change""" - param_letters = set('T') - word_key = Word('M', 6) - word_letter = 'M' - exec_order = 80 - - -class GCodeToolSetCurrent(GCodeNonModal): - """M61: Set Current Tool""" - param_letters = set('Q') - word_key = Word('M', 61) - word_letter = 'M' - exec_order = 80 - - -class GCodeSet(GCodeNonModal): - """G10: Set stuff""" - param_letters = set('LPQRABCXYZ') - word_key = Word('G', 10) - exec_order = 230 - - -class GCodeGotoPredefinedPosition(GCodeNonModal): - """G28,G30: Goto Predefined Position (rapid movement)""" - param_letters = set('W') - @classmethod - def word_matches(cls, w): - return (w.letter == 'G') and (w.value in [28, 30]) - default_word = Word('G', 28) - exec_order = 230 - - -class GCodeSetPredefinedPosition(GCodeNonModal): - """G28.1,G30.1: Set Predefined Position""" # redundancy in language there, but I'll let it slide - param_letters = set('W') - @classmethod - def word_matches(cls, w): - return (w.letter == 'G') and (w.value in [28.1, 30.1]) - default_word = Word('G', 28.1) - exec_order = 230 - - -class GCodeMoveInMachineCoords(GCodeNonModal): - """G53: Move in Machine Coordinates""" - word_key = Word('G', 53) - exec_order = 240 - - -class GCodeCoordSystemOffset(GCodeNonModal): - """G92: Coordinate System Offset""" - param_letters = set('XYZABCUVW') - word_key = Word('G', 92) - exec_order = 230 - - -class GCodeResetCoordSystemOffset(GCodeNonModal): - """G92.1,G92.2: Reset Coordinate System Offset""" - @classmethod - def word_matches(cls, w): - return (w.letter == 'G') and (w.value in [92.1, 92.2]) - default_word = Word('G', 92.1) - exec_order = 230 - - # TODO: machine.state.offset *= 0 - - -class GCodeRestoreCoordSystemOffset(GCodeNonModal): - """G92.3: Restore Coordinate System Offset""" - word_key = Word('G', 92.3) - exec_order = 230 - - -class GCodeUserDefined(GCodeNonModal): - """M101-M199: User Defined Commands""" - word_letter = 'M' - # To create user g-codes, inherit from this class - param_letters = set('PQ') - #@classmethod - #def word_matches(cls, w): - # return (w.letter == 'M') and (101 <= w.value <= 199) - #default_word = Word('M', 101) - exec_order = 130 - modal_group = MODAL_GROUP_MAP['user_defined'] - -# ======================= Prusa ======================= -# CODE PARAMETERS DESCRIPTION -# M862.1 P Q Nozzle Diameter -# M862.2 P Q Model Code -# M862.3 P Q Model Name -# M862.4 P Q Firmware Version -# M862.5 P Q GCode Level -# M115 V U Firmware info -# M73 P R Q S C D Set/Get print progress -# M205 S T B X Y Z E Set advanced settings -# M104 S Set extruder temperature -# M109 B R S Wait for extruder temperature -# M140 S Set bed temperature -# M190 R S Wait for bed temperature -# M204 S T Acceleration settings -# M221 S T Set extrude factor override percentage -# M106 S Set fan speed -# G80 N R V L R F B Mesh-based Z probe - -class GCodePrintChecking(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('PQ') - -class GCodeNozzleDiameterPrintChecking(GCodePrintChecking): - """M862.1: Nozzle Diameter""" - word_key = Word('M', 862.1) - -class GCodeModelCodePrintChecking(GCodePrintChecking): - """M862.2: Model Code""" - word_key = Word('M', 862.2) - -class GCodeModelNamePrintChecking(GCodePrintChecking): - """M862.3: Model Name""" - word_key = Word('M', 862.3) - -class GCodeFirmwareVersionPrintChecking(GCodePrintChecking): - """M862.4: Firmware Version""" - word_key = Word('M', 862.4) - -class GCodeGcodeLevelPrintChecking(GCodePrintChecking): - """M862.5: Gcode Level""" - word_key = Word('M', 862.5) - -class GCodeFirmwareInfo(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('VU') - word_key = Word('M', 115) - -class GCodePrintProgress(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('PRQSCD') - word_key = Word('M', 73) - -class GCodeSetAdvancedSettings(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('STBXYZE') - word_key = Word('M', 205) - -class GCodeSetExtruderTemperature(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('S') - word_key = Word('M', 104) - -class GCodeWaitForExtruderTemperature(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('BRS') - word_key = Word('M', 109) - -class GCodeSetBedTemperature(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('S') - word_key = Word('M', 140) - -class GCodeWaitForBedTemperature(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('RS') - word_key = Word('M', 190) - -class GCodeAccelerationSettings(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('ST') - word_key = Word('M', 204) - -class GCodeSetExtrudeFactorOverridePercentage(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('ST') - word_key = Word('M', 221) - -class GCodeSetFanSpeed(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('S') - word_key = Word('M', 106) - -class GCodeMeshBasedZProbe(GCode): - exec_order = 999 - modal_group = MODAL_GROUP_MAP['user_defined'] - param_letters = set('NRVLRFB') - word_key = Word('G', 80) +match get_default_dialect(): + case 'marlin2': + from .gcodes_marlin import * + case 'prusa': + from .gcodes_prusa import * + case _: + from .gcodes_lecacy import * # ======================= Utilities ======================= @@ -1534,6 +209,10 @@ def build_maps(): _gcode_function_list = [] for cls in _subclasses(GCode): + try: + if get_default_dialect() not in cls.dialects: continue + except AttributeError: + pass if cls.word_key is not None: # Map Word instance to g-code class if cls.word_key in _gcode_word_map: diff --git a/src/pygcode/gcodes_base.py b/src/pygcode/gcodes_base.py new file mode 100644 index 0000000..7324a90 --- /dev/null +++ b/src/pygcode/gcodes_base.py @@ -0,0 +1,423 @@ +class GCode(object): + # Defining Word + word_key = None # Word instance to use in lookup + word_matches = None # function (secondary) + default_word = None + word_letter = 'G' + word_value_configurable = False # if set, word value can be the first parameter + + # Parameters associated to this gcode + param_letters = set() + + # Modal stuff + modal_group = None + modal_param_letters = set() # by default: no parameters are retained in modal state + + # Execution Order + exec_order = 999 # if not otherwise specified, run last + + def __init__(self, *words, **params): + """ + :param word: Word instance defining gcode (eg: Word('G0') for rapid movement) + :param params: list of Word instances (eg: Word('X-1.2') as x-coordinate) + """ + gcode_word_list = words[:1] + param_words = words[1:] + if gcode_word_list: + gcode_word = gcode_word_list[0] + if self.word_value_configurable and isinstance(gcode_word, six.integer_types + (float,)): + gcode_word = Word(self.word_letter, gcode_word) # cast to Word() + else: + gcode_word = self._default_word() + assert isinstance(gcode_word, Word), "invalid gcode word %r" % gcode_word + self.word = gcode_word + self.params = {} + + # Whitespace as prefix + # if True, str(self) will repalce self.word code with whitespace + self._whitespace_prefix = False + + # Add Given Parameters + for param_word in param_words: + self.add_parameter(param_word) + for (k, v) in params.items(): + self.add_parameter(Word(k, v)) + + def __repr__(self): + param_str = '' + if self.params: + param_str = "{%s}" % (', '.join([ + "{}".format(self.params[k]) + for k in sorted(self.params.keys()) + ])) + return "<{class_name}: {gcode}{params}>".format( + class_name=self.__class__.__name__, + gcode=self.word, + params=param_str, + ) + + def __str__(self): + """String representation of gcode, as it would be seen in a .gcode file""" + param_str = '' + if self.params: + param_str += ' ' + ' '.join([ + "{}".format(self.params[k]) + for k in sorted(self.params.keys()) + ]) + word_str = str(self.word) + if self._whitespace_prefix: + word_str = ' ' * len(word_str) + return "{word_str}{parameters}".format( + word_str=word_str, + parameters=param_str, + ) + + def __hash__(self): + """Hash representation of the gcode, for set and dictionary usage""" + try: + return hash(self.word_key) + except TypeError: + return hash(self.word_letter) # May also want to retrieve additional value info + + + def _default_word(self): + if self.default_word: + return copy(self.default_word) + elif self.word_key: + return copy(self.word_key) + raise AssertionError("class %r has no default word" % self.__class__) + + # Equality + def __eq__(self, other): + return ( + (self.word == other.word) and + (self.params == other.params) + ) + + def __ne__(self, other): + return not self.__eq__(other) + + # Sort by execution order + def __lt__(self, other): + return self.exec_order < other.exec_order + + def __le__(self, other): + return self.exec_order <= other.exec_order + + def __gt__(self, other): + return self.exec_order > other.exec_order + + def __ge__(self, other): + return self.exec_order >= other.exec_order + + # Parameters + def add_parameter(self, word): + """ + Add given word as a parameter for this gcode + :param word: Word instance + """ + assert isinstance(word, Word), "invalid parameter class: %r" % word + if word.letter not in self.param_letters: + raise GCodeParameterError("invalid parameter for %s: %s" % (self.__class__.__name__, str(word))) + if word.letter in self.params: + raise GCodeParameterError("parameter defined twice: %s -> %s" % (self.params[word.letter], word)) + + self.params[word.letter] = word + + # Assert Parameters + def assert_params(self): + """ + Assert validity of gcode's parameters. + This verification is irrespective of machine, or machine's state; + verification is g-code language-based verification + :raises: GCodeParameterError + """ + # to be overridden in inheriting classes + pass + + def __getattr__(self, key): + # Return parameter values (if valid parameter for gcode) + if key in self.param_letters: + if key in self.params: + return self.params[key].value + else: + return None # parameter is valid for GCode, but undefined + + raise AttributeError("'{cls}' object has no attribute '{key}'".format( + cls=self.__class__.__name__, + key=key + )) + + def __setattr__(self, key, value): + if key in self.param_letters: + if key in self.params: + self.params[key].value = value + else: + self.add_parameter(Word(key, value)) + + else: + self.__dict__[key] = value + + @property + def description(self): + return self.__doc__ + + def modal_copy(self): + """Copy of GCode instance containing only parameters listed in modal_param_letters""" + return self.__class__(self.word, *[ + w for (l, w) in self.params.items() + if l in self.modal_param_letters + ]) + + def get_param_dict(self, letters=None, lc=False): + """ + Get gcode parameters as a dict + gcode parameter like "X3.1, Y-2" would return {'X': 3.1, 'Y': -2} + :param letters: iterable whitelist of letters to include as dict keys + :param lc: lower case parameter letters + :return: dict of gcode parameters' (letter, value) pairs + """ + letter_mod = lambda x: x + if lc: + letter_mod = lambda x: x.lower() + return dict( + (letter_mod(w.letter), w.value) for w in self.params.values() + if (letters is None) or (w.letter in letters) + ) + + # Process GCode + def process(self, machine): + """ + Process a GCode on the given Machine + :param machine: Machine instance, to change state + :return: GCodeEffect instance; effect the gcode just had on machine + """ + from .machine import Machine # importing up (done live to avoid dependency loop) + assert isinstance(machine, Machine), "invalid machine type: %r" % machine + + # Set mode + self._process_mode(machine) + + # GCode specific + self._process(machine) + + def _process_mode(self, machine): + """Set machine's state""" + machine.set_mode(self) + + def _process(self, machine): + """Process this GCode (to be overridden)""" + pass + + +# ======================= Non Operational ======================= +# CODE PARAMETERS DESCRIPTION +# N# Define line number (oldschool) +# O Define program name + +class GCodeDefinition(GCode): + pass + + +class GCodeLineNumber(GCodeDefinition): + """N: Line Number""" + word_letter = 'N' + word_value_configurable = True + exec_order = 0 + + @classmethod + def word_matches(cls, w): + return w.letter == 'N' + + @property + def number(self): + return self.word.value + + +class GCodeProgramName(GCodeDefinition): + """O: Program Name""" + word_letter = 'O' + word_value_configurable = True + exec_order = 1 + + @classmethod + def word_matches(cls, w): + return w.letter == 'O' + + @property + def name(self): + return self.word.value + + +# ======================= Motion ======================= +class GCodeMotion(GCode): + param_letters = set('XYZABCUVW') + modal_group = MODAL_GROUP_MAP['motion'] + exec_order = 242 + + def _process(self, machine): + machine.move_to(**self.get_param_dict(letters=machine.axes)) + + +class GCodeArcMove(GCodeMotion): + """Arc Move""" + param_letters = GCodeMotion.param_letters | set('IJKRP') + + def assert_params(self): + param_letters = set(self.params.keys()) + # Parameter groups + params_xyz = set('XYZ') & set(param_letters) + params_ijk = set('IJK') & set(param_letters) + params_r = set('R') & set(param_letters) + params_ijkr = params_ijk | params_r + + # --- Parameter Groups + # XYZ: at least 1 + if not params_xyz: + raise GCodeParameterError("no XYZ parameters set for destination: %r" % arc_gcode) + # IJK or R: only in 1 group + if params_ijk and params_r: + raise GCodeParameterError("both IJK and R parameters defined: %r" % arc_gcode) + # IJKR: at least 1 + if not params_ijkr: + raise GCodeParameterError("neither IJK or R parameters defined: %r" % arc_gcode) + + # --- Parameter Values + if params_r and (self.R == 0): + raise GCodeParameterError("cannot plot a circle with a radius of zero: %r" % arc_gcode) + +# ======================= Canned Cycles ======================= +class GCodeCannedCycle(GCode): + param_letters = set('XYZUVW') + modal_group = MODAL_GROUP_MAP['canned_cycle'] + exec_order = 242 + + def _process(self, machine): + moveto_coords = self.get_param_dict(letters=machine.axes) + if isinstance(machine.mode.canned_cycles_return, GCodeCannedCycleReturnToR): + # canned return is to this.R, not this.Z (plane dependent) + moveto_coords.update({ + machine.mode.plane_selection.normal_axis: self.R, + }) + else: # default: GCodeCannedCycleReturnPrevLevel + # Remove this.Z (plane dependent) value (ie: no machine movement on this axis) + moveto_coords.pop(machine.mode.plane_selection.normal_axis, None) + + # Process action 'L' times + if hasattr(self, 'L'): + loop_count = self.L + if (loop_count is None) or (loop_count <= 0): + loop_count = 1 + for i in range(loop_count): + machine.move_to(**moveto_coords) + + +# ======================= Distance Mode ======================= +class GCodeDistanceMode(GCode): + exec_order = 210 + + +# ======================= Feed Rate Mode ======================= +class GCodeFeedRateMode(GCode): + modal_group = MODAL_GROUP_MAP['feed_rate_mode'] + exec_order = 30 + + +# ======================= Spindle Control ======================= +class GCodeSpindle(GCode): + word_letter = 'M' + exec_order = 90 + + +# ======================= Coolant ======================= +class GCodeCoolant(GCode): + word_letter = 'M' + modal_group = MODAL_GROUP_MAP['coolant'] + exec_order = 110 + + +# ======================= Tool Length ======================= +class GCodeToolLength(GCode): + modal_group = MODAL_GROUP_MAP['tool_length_offset'] + exec_order = 180 + + +# ======================= Stopping (Program Control) ======================= +class GCodeProgramControl(GCode): + word_letter = 'M' + modal_group = MODAL_GROUP_MAP['stopping'] + exec_order = 250 + +# ======================= Units ======================= +class GCodeUnit(GCode): + modal_group = MODAL_GROUP_MAP['units'] + exec_order = 160 + +# ======================= Plane Selection ======================= +class GCodePlaneSelect(GCode): + modal_group = MODAL_GROUP_MAP['plane_selection'] + exec_order = 150 + + # -- Plane Orientation Quaternion + # Such that... + # vectorXY = Vector3() + # vectorZX = GCodeSelectZXPlane.quat * vectorXY + # vectorZX += some_offset_vector + # vectorXY = GCodeSelectZXPlane.quat.conjugate() * vectorZX + # note: all quaternions use the XY plane as a basis + # To transform from ZX to YZ planes via these quaternions, you must + # first translate it to XY, like so: + # vectorYZ = GCodeSelectYZPlane.quat * (GCodeSelectZXPlane.quat.conjugate() * vectorZX) + quat = None # Quaternion + + # -- Plane Axis Information + # Vector normal to plane (such that XYZ axes follow the right-hand rule) + normal_axis = None # Letter of normal axis (upper case) + # Axes of plane + plane_axes = set() + normal = None # Vector3 + +# ======================= Cutter Radius Compensation ======================= +class GCodeCutterRadiusComp(GCode): + modal_group = MODAL_GROUP_MAP['cutter_diameter_comp'] + exec_order = 170 + +# ======================= Path Control Mode ======================= +class GCodePathControlMode(GCode): + modal_group = MODAL_GROUP_MAP['control_mode'] + exec_order = 200 + +# ======================= Return Mode in Canned Cycles ======================= +class GCodeCannedReturnMode(GCode): + modal_group = MODAL_GROUP_MAP['canned_cycles_return'] + exec_order = 220 + + +# ======================= Other Modal Codes ======================= +class GCodeOtherModal(GCode): + pass + + +class GCodeSelectCoordinateSystem(GCodeOtherModal): + """Select Coordinate System""" + modal_group = MODAL_GROUP_MAP['coordinate_system'] + exec_order = 190 + coord_system_id = None # overridden in inheriting classes + + +# ======================= Flow-control Codes ======================= +class GCodeIO(GCode): + word_letter = 'M' + exec_order = 70 + + +class GCodeDigitalOutput(GCodeIO): + """Digital Output Control""" + param_letters = set('P') + + +# ======================= Non-modal Codes ======================= +class GCodeNonModal(GCode): + pass + + diff --git a/src/pygcode/gcodes_legacy.py b/src/pygcode/gcodes_legacy.py new file mode 100644 index 0000000..3266640 --- /dev/null +++ b/src/pygcode/gcodes_legacy.py @@ -0,0 +1,821 @@ +from .gcodes_base import * + +# ======================= Motion ======================= +# (X Y Z A B C U V W apply to all motions) +# CODE PARAMETERS DESCRIPTION +# G0 Rapid Move +# G1 Linear Move +# G2, G3 I J K or R, P Arc Move +# G4 P Dwell +# G5 I J P Q Cubic Spline +# G5.1 I J Quadratic Spline +# G5.2 P L NURBS +# G10 Retract +# G11 Recover +# G38.2 - G38.5 Straight Probe +# G33 K Spindle Synchronized Motion +# G33.1 K Rigid Tapping + +class GCodeRapidMove(GCodeMotion): + """G0: Rapid Move""" + word_key = Word('G', 0) + + def _process(self, machine): + machine.move_to(rapid=True, **self.get_param_dict(letters=machine.axes)) + + +class GCodeLinearMove(GCodeMotion): + """G1: Linear Move""" + word_key = Word('G', 1) + + + +class GCodeArcMoveCW(GCodeArcMove): + """G2: Arc Move (clockwise)""" + word_key = Word('G', 2) + + +class GCodeArcMoveCCW(GCodeArcMove): + """G3: Arc Move (counter-clockwise)""" + word_key = Word('G', 3) + + +class GCodeDwell(GCodeMotion): + """G4: Dwell""" + param_letters = set('P') # doesn't accept axis parameters + word_key = Word('G', 4) + modal_group = None # one of the few motion commands that isn't modal + exec_order = 140 + + def _process(self, machine): + pass # no movements made + + +class GCodeCublcSpline(GCodeMotion): + """G5: Cubic Spline""" + param_letters = GCodeMotion.param_letters | set('IJPQ') + word_key = Word('G', 5) + + +class GCodeQuadraticSpline(GCodeMotion): + """G5.1: Quadratic Spline""" + param_letters = GCodeMotion.param_letters | set('IJ') + word_key = Word('G', 5.1) + + +class GCodeNURBS(GCodeMotion): + """G5.2: Non-uniform rational basis spline (NURBS)""" + param_letters = GCodeMotion.param_letters | set('PL') + word_key = Word('G', 5.2) + + +class GCodeNURBSEnd(GCodeNURBS): + """G5.3: end NURBS mode""" + word_key = Word('G', 5.3) + +class GCodeStraightProbe(GCodeMotion): + """G38.2-G38.5: Straight Probe""" + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (38.2 <= w.value <= 38.5) + default_word = Word('G', 38.2) + + +class GCodeSpindleSyncMotion(GCodeMotion): + """G33: Spindle Synchronized Motion""" + param_letters = GCodeMotion.param_letters | set('K') + word_key = Word('G', 33) + + +class GCodeRigidTapping(GCodeMotion): + """G33.1: Rigid Tapping""" + param_letters = GCodeMotion.param_letters | set('K') + word_key = Word('G', 33.1) + + +# ======================= Canned Cycles ======================= +# (X Y Z or U V W apply to canned cycles, depending on active plane) +# CODE PARAMETERS DESCRIPTION +# G81 R L (P) Drilling Cycle +# G82 R L (P) Drilling Cycle, Dwell +# G83 R L Q Drilling Cycle, Peck +# G73 R L Q Drilling Cycle, Chip Breaking +# G85 R L (P) Boring Cycle, Feed Out +# G89 R L (P) Boring Cycle, Dwell, Feed Out +# G76 P Z I J R K Q H L E Threading Cycle +# G80 Cancel Canned Cycle + +class GCodeDrillingCycle(GCodeCannedCycle): + """G81: Drilling Cycle""" + param_letters = GCodeCannedCycle.param_letters | set('RLP') + word_key = Word('G', 81) + modal_param_letters = GCodeCannedCycle.param_letters | set('RP') + + +class GCodeDrillingCycleDwell(GCodeCannedCycle): + """G82: Drilling Cycle, Dwell""" + param_letters = GCodeCannedCycle.param_letters | set('RLP') + word_key = Word('G', 82) + modal_param_letters = GCodeCannedCycle.param_letters | set('RP') + + +class GCodeDrillingCyclePeck(GCodeCannedCycle): + """G83: Drilling Cycle, Peck""" + param_letters = GCodeCannedCycle.param_letters | set('RLQ') | set('IJK') + word_key = Word('G', 83) + modal_param_letters = GCodeCannedCycle.param_letters | set('RQ') + + +class GCodeDrillingCycleChipBreaking(GCodeCannedCycle): + """G73: Drilling Cycle, ChipBreaking""" + param_letters = GCodeCannedCycle.param_letters | set('RLQ') + word_key = Word('G', 73) + modal_param_letters = GCodeCannedCycle.param_letters | set('RQ') + + +class GCodeBoringCycleFeedOut(GCodeCannedCycle): + """G85: Boring Cycle, Feed Out""" + param_letters = GCodeCannedCycle.param_letters | set('RLP') + word_key = Word('G', 85) + modal_param_letters = GCodeCannedCycle.param_letters | set('RP') + + +class GCodeBoringCycleDwellFeedOut(GCodeCannedCycle): + """G89: Boring Cycle, Dwell, Feed Out""" + param_letters = GCodeCannedCycle.param_letters | set('RLP') + word_key = Word('G', 89) + modal_param_letters = GCodeCannedCycle.param_letters | set('RP') + + +class GCodeThreadingCycle(GCodeCannedCycle): + """G76: Threading Cycle""" + param_letters = GCodeCannedCycle.param_letters | set('PZIJRKQHLE') + word_key = Word('G', 76) + +class GCodeCancelCannedCycle(GCodeCannedCycle): + """ G80: Cancel Canned Cycle """ + dialects = DIALECT_UNKNOWN + param_letters = set() + word_key = Word('G', 80) + # Modal Group + # Technically G80 belongs to the motion modal group, however it's often + # expressed in the same line as another motion command. + # This is alowed, but executed just prior to any other motion command + # eg: G00 G80 + # will leave the machine in rapid motion mode + # Just running G80 will leave machine with no motion mode. + exec_order = 241 + + +# ======================= Distance Mode ======================= +# CODE PARAMETERS DESCRIPTION +# G90, G91 Distance Mode +# G90.1, G91.1 Arc Distance Mode +# G7 Lathe Diameter Mode +# G8 Lathe Radius Mode + +class GCodeAbsoluteDistanceMode(GCodeDistanceMode): + """G90: Absolute Distance Mode""" + word_key = Word('G', 90) + modal_group = MODAL_GROUP_MAP['distance'] + + +class GCodeIncrementalDistanceMode(GCodeDistanceMode): + """G91: Incremental Distance Mode""" + word_key = Word('G', 91) + modal_group = MODAL_GROUP_MAP['distance'] + + +class GCodeAbsoluteArcDistanceMode(GCodeDistanceMode): + """G90.1: Absolute Distance Mode for Arc IJK Parameters""" + word_key = Word('G', 90.1) + modal_group = MODAL_GROUP_MAP['arc_ijk_distance'] + + +class GCodeIncrementalArcDistanceMode(GCodeDistanceMode): + """G91.1: Incremental Distance Mode for Arc IJK Parameters""" + word_key = Word('G', 91.1) + modal_group = MODAL_GROUP_MAP['arc_ijk_distance'] + + +class GCodeLatheDiameterMode(GCodeDistanceMode): + """G7: Lathe Diameter Mode""" + word_key = Word('G', 7) + modal_group = MODAL_GROUP_MAP['lathe_diameter'] + + +class GCodeLatheRadiusMode(GCodeDistanceMode): + """G8: Lathe Radius Mode""" + word_key = Word('G', 8) + modal_group = MODAL_GROUP_MAP['lathe_diameter'] + + +# ======================= Feed Rate Mode ======================= +# CODE PARAMETERS DESCRIPTION +# G93, G94, G95 Feed Rate Mode + +class GCodeInverseTimeMode(GCodeFeedRateMode): + """G93: Inverse Time Mode""" + word_key = Word('G', 93) + + +class GCodeUnitsPerMinuteMode(GCodeFeedRateMode): + """G94: Units Per MinuteMode""" + word_key = Word('G', 94) + + +class GCodeUnitsPerRevolution(GCodeFeedRateMode): + """G95: Units Per Revolution""" + word_key = Word('G', 95) + + +# ======================= Spindle Control ======================= +# CODE PARAMETERS DESCRIPTION +# M3, M4, M5 S Spindle Control +# M19 Orient Spindle +# G96, G97 S D Spindle Control Mode + +class GCodeStartSpindle(GCodeSpindle): + """M3,M4: Start Spindle Clockwise""" + modal_group = MODAL_GROUP_MAP['spindle'] + + +class GCodeStartSpindleCW(GCodeStartSpindle): + """M3: Start Spindle Clockwise""" + #param_letters = set('S') # S is it's own gcode, makes no sense to be here + word_key = Word('M', 3) + +class GCodeStartSpindleCCW(GCodeStartSpindle): + """M4: Start Spindle Counter-Clockwise""" + #param_letters = set('S') # S is it's own gcode, makes no sense to be here + word_key = Word('M', 4) + + +class GCodeStopSpindle(GCodeSpindle): + """M5: Stop Spindle""" + #param_letters = set('S') # S is it's own gcode, makes no sense to be here + word_key = Word('M', 5) + modal_group = MODAL_GROUP_MAP['spindle'] + + +class GCodeOrientSpindle(GCodeSpindle): + """M19: Orient Spindle""" + word_key = Word('M', 19) + + +class GCodeSpindleSpeedMode(GCodeSpindle): + word_letter = 'G' + modal_group = MODAL_GROUP_MAP['spindle_speed_mode'] + + +class GCodeSpindleConstantSurfaceSpeedMode(GCodeSpindleSpeedMode): + """G96: Spindle Constant Surface Speed""" + param_letters = set('DS') + word_key = Word('G', 96) + + +class GCodeSpindleRPMMode(GCodeSpindleSpeedMode): + """G97: Spindle RPM Speed""" + param_letters = set('D') + word_key = Word('G', 97) + + + +# ======================= Coolant ======================= +# CODE PARAMETERS DESCRIPTION +# M7, M8, M9 Coolant Control + +class GCodeCoolantMistOn(GCodeCoolant): + """M7: turn mist coolant on""" + word_key = Word('M', 7) + + +class GCodeCoolantFloodOn(GCodeCoolant): + """M8: turn flood coolant on""" + word_key = Word('M', 8) + + +class GCodeCoolantOff(GCodeCoolant): + """M9: turn all coolant off""" + word_key = Word('M', 9) + + +# ======================= Tool Length ======================= +# CODE PARAMETERS DESCRIPTION +# G43 H Tool Length Offset +# G43.1 Dynamic Tool Length Offset +# G43.2 H Apply additional Tool Length Offset +# G49 Cancel Tool Length Compensation + +class GCodeToolLengthOffset(GCodeToolLength): + """G43: Tool Length Offset""" + param_letters = set('H') + word_key = Word('G', 43) + + +class GCodeDynamicToolLengthOffset(GCodeToolLength): + """G43.1: Dynamic Tool Length Offset""" + word_key = Word('G', 43.1) + + +class GCodeAddToolLengthOffset(GCodeToolLength): + """G43.2: Appkly Additional Tool Length Offset""" + param_letters = set('H') + word_key = Word('G', 43.2) + + +class GCodeCancelToolLengthOffset(GCodeToolLength): + """G49: Cancel Tool Length Compensation""" + word_key = Word('G', 49) + + +# ======================= Stopping (Program Control) ======================= +# CODE PARAMETERS DESCRIPTION +# M0, M1 Program Pause +# M2, M30 Program End +# M60 Pallet Change Pause + +class GCodePauseProgram(GCodeProgramControl): + """M0: Program Pause""" + word_key = Word('M', 0) + + +class GCodePauseProgramOptional(GCodeProgramControl): + """M1: Program Pause (optional)""" + word_key = Word('M', 1) + + +class GCodeEndProgram(GCodeProgramControl): + """M2: Program End""" + word_key = Word('M', 2) + + +class GCodeEndProgramPalletShuttle(GCodeProgramControl): + """M30: exchange pallet shuttles and end the program""" + word_key = Word('M', 30) + + +class GCodePalletChangePause(GCodeProgramControl): + """M60: Pallet Change Pause""" + word_key = Word('M', 60) + + +# ======================= Units ======================= +# CODE PARAMETERS DESCRIPTION +# G20, G21 Units + +class GCodeUseInches(GCodeUnit): + """G20: use inches for length units""" + word_key = Word('G', 20) + unit_id = 0 + + +class GCodeUseMillimeters(GCodeUnit): + """G21: use millimeters for length units""" + word_key = Word('G', 21) + unit_id = 1 + + +# ======================= Plane Selection ======================= +# (affects G2, G3, G81-G89, G40-G42) +# CODE PARAMETERS DESCRIPTION +# G17 - G19.1 Plane Select + +class GCodeSelectXYPlane(GCodePlaneSelect): + """G17: select XY plane (default)""" + word_key = Word('G', 17) + quat = Quaternion() # no effect + normal_axis = 'Z' + plane_axes = set('XY') + normal = Vector3(0., 0., 1.) + + +class GCodeSelectZXPlane(GCodePlaneSelect): + """G18: select ZX plane""" + word_key = Word('G', 18) + quat = quat2coord_system( + Vector3(1., 0., 0.), Vector3(0., 1., 0.), + Vector3(0., 0., 1.), Vector3(1., 0., 0.) + ) + normal_axis = 'Y' + plane_axes = set('ZX') + normal = Vector3(0., 1., 0.) + + +class GCodeSelectYZPlane(GCodePlaneSelect): + """G19: select YZ plane""" + word_key = Word('G', 19) + quat = quat2coord_system( + Vector3(1., 0., 0.), Vector3(0., 1., 0.), + Vector3(0., 1., 0.), Vector3(0., 0., 1.) + ) + normal_axis = 'X' + plane_axes = set('YZ') + normal = Vector3(1., 0., 0.) + + +class GCodeSelectUVPlane(GCodePlaneSelect): + """G17.1: select UV plane""" + word_key = Word('G', 17.1) + + +class GCodeSelectWUPlane(GCodePlaneSelect): + """G18.1: select WU plane""" + word_key = Word('G', 18.1) + + +class GCodeSelectVWPlane(GCodePlaneSelect): + """G19.1: select VW plane""" + word_key = Word('G', 19.1) + + +# ======================= Cutter Radius Compensation ======================= +# CODE PARAMETERS DESCRIPTION +# G40 Compensation Off +# G41,G42 D Cutter Compensation +# G41.1, G42.1 D L Dynamic Cutter Compensation + +class GCodeCutterRadiusCompOff(GCodeCutterRadiusComp): + """G40: Cutter Radius Compensation Off""" + word_key = Word('G', 40) + + +class GCodeCutterCompLeft(GCodeCutterRadiusComp): + """G41: Cutter Radius Compensation (left)""" + param_letters = set('D') + word_key = Word('G', 41) + + +class GCodeCutterCompRight(GCodeCutterRadiusComp): + """G42: Cutter Radius Compensation (right)""" + param_letters = set('D') + word_key = Word('G', 42) + + +class GCodeDynamicCutterCompLeft(GCodeCutterRadiusComp): + """G41.1: Dynamic Cutter Radius Compensation (left)""" + param_letters = set('DL') + word_key = Word('G', 41.1) + + +class GCodeDynamicCutterCompRight(GCodeCutterRadiusComp): + """G42.1: Dynamic Cutter Radius Compensation (right)""" + param_letters = set('DL') + word_key = Word('G', 42.1) + +# ======================= Path Control Mode ======================= +# CODE PARAMETERS DESCRIPTION +# G61 G61.1 Exact Path Mode +# G64 P Q Path Blending + + +class GCodeExactPathMode(GCodePathControlMode): + """G61: Exact path mode""" + word_key = Word('G', 61) + + +class GCodeExactStopMode(GCodePathControlMode): + """G61.1: Exact stop mode""" + word_key = Word('G', 61.1) + + +class GCodePathBlendingMode(GCodePathControlMode): + """G64: Path Blending""" + param_letters = set('PQ') + word_key = Word('G', 64) + + +# ======================= Return Mode in Canned Cycles ======================= +# CODE PARAMETERS DESCRIPTION +# G98 Canned Cycle Return Level to previous +# G99 Canned Cycle Return to the level set by R + +class GCodeCannedCycleReturnPrevLevel(GCodeCannedReturnMode): + """G98: Canned Cycle Return to the level set prior to cycle start""" + # "retract to the position that axis was in just before this series of one or more contiguous canned cycles was started" + word_key = Word('G', 98) + + +class GCodeCannedCycleReturnToR(GCodeCannedReturnMode): + """G99: Canned Cycle Return to the level set by R""" + # "retract to the position specified by the R word of the canned cycle" + word_key = Word('G', 99) + + +# ======================= Other Modal Codes ======================= +# CODE PARAMETERS DESCRIPTION +# F Set Feed Rate +# S Set Spindle Speed +# T Select Tool +# M48, M49 Speed and Feed Override Control +# M50 P0 (off) or P1 (on) Feed Override Control +# M51 P0 (off) or P1 (on) Spindle Speed Override Control +# M52 P0 (off) or P1 (on) Adaptive Feed Control +# M53 P0 (off) or P1 (on) Feed Stop Control +# G54-G59.3 Select Coordinate System + +class GCodeFeedRate(GCodeOtherModal): + """F: Set Feed Rate""" + word_letter = 'F' + word_value_configurable = True + @classmethod + def word_matches(cls, w): + return w.letter == 'F' + default_word = Word('F', 0) + modal_group = MODAL_GROUP_MAP['feed_rate'] + exec_order = 40 + + +class GCodeSpindleSpeed(GCodeOtherModal): + """S: Set Spindle Speed""" + word_letter = 'S' + word_value_configurable = True + @classmethod + def word_matches(cls, w): + return w.letter == 'S' + default_word = Word('S', 0) + # Modal Group: (see description in GCodeFeedRate) + modal_group = MODAL_GROUP_MAP['spindle_speed'] + exec_order = 50 + + +class GCodeSelectTool(GCodeOtherModal): + """T: Select Tool""" + word_letter = 'T' + word_value_configurable = True + @classmethod + def word_matches(cls, w): + return w.letter == 'T' + default_word = Word('T', 0) + # Modal Group: (see description in GCodeFeedRate) + modal_group = MODAL_GROUP_MAP['tool'] + exec_order = 60 + + +class GCodeSpeedAndFeedOverrideOn(GCodeOtherModal): + """M48: Speed and Feed Override Control On""" + word_letter = 'M' + word_key = Word('M', 48) + modal_group = MODAL_GROUP_MAP['override_switches'] + exec_order = 120 + + +class GCodeSpeedAndFeedOverrideOff(GCodeOtherModal): + """M49: Speed and Feed Override Control Off""" + word_letter = 'M' + word_key = Word('M', 49) + modal_group = MODAL_GROUP_MAP['override_switches'] + exec_order = 120 + + +class GCodeFeedOverride(GCodeOtherModal): + """M50: Feed Override Control""" + word_letter = 'M' + param_letters = set('P') + word_key = Word('M', 50) + exec_order = 120 + + +class GCodeSpindleSpeedOverride(GCodeOtherModal): + """M51: Spindle Speed Override Control""" + word_letter = 'M' + param_letters = set('P') + word_key = Word('M', 51) + exec_order = 120 + + +class GCodeAdaptiveFeed(GCodeOtherModal): + """M52: Adaptive Feed Control""" + word_letter = 'M' + param_letters = set('P') + word_key = Word('M', 52) + exec_order = 120 + + +class GCodeFeedStop(GCodeOtherModal): + """M53: Feed Stop Control""" + word_letter = 'M' + param_letters = set('P') + word_key = Word('M', 53) + exec_order = 120 + + +class GCodeSelectCoordinateSystem1(GCodeSelectCoordinateSystem): + """Select Coordinate System 1""" + word_key = Word('G', 54) + coord_system_id = 1 + + +class GCodeSelectCoordinateSystem2(GCodeSelectCoordinateSystem): + """Select Coordinate System 2""" + word_key = Word('G', 55) + coord_system_id = 2 + + +class GCodeSelectCoordinateSystem3(GCodeSelectCoordinateSystem): + """Select Coordinate System 3""" + word_key = Word('G', 56) + coord_system_id = 3 + + +class GCodeSelectCoordinateSystem4(GCodeSelectCoordinateSystem): + """Select Coordinate System 4""" + word_key = Word('G', 57) + coord_system_id = 4 + + +class GCodeSelectCoordinateSystem5(GCodeSelectCoordinateSystem): + """Select Coordinate System 5""" + word_key = Word('G', 58) + coord_system_id = 5 + + +class GCodeSelectCoordinateSystem6(GCodeSelectCoordinateSystem): + """Select Coordinate System 6""" + word_key = Word('G', 59) + coord_system_id = 6 + + +class GCodeSelectCoordinateSystem7(GCodeSelectCoordinateSystem): + """Select Coordinate System 7""" + coord_system_id = 7 + word_key = Word('G', 59.1) + + +class GCodeSelectCoordinateSystem8(GCodeSelectCoordinateSystem): + """Select Coordinate System 8""" + word_key = Word('G', 59.2) + coord_system_id = 8 + + +class GCodeSelectCoordinateSystem9(GCodeSelectCoordinateSystem): + """G9: Select Coordinate System 9""" + word_key = Word('G', 59.3) + coord_system_id = 9 + + +# ======================= Flow-control Codes ======================= +# CODE PARAMETERS DESCRIPTION +# o sub Subroutines, sub/endsub call [unsupported] +# o while Looping, while/endwhile do/while [unsupported] +# o if Conditional, if/else/endif [unsupported] +# o repeat Repeat a loop of code [unsupported] +# [] Indirection [unsupported] +# o call Call named file [unsupported] +# M70 Save modal state [unsupported] +# M71 Invalidate stored state [unsupported] +# M72 Restore modal state [unsupported] +# M73 Save and Auto-restore modal state [unsupported] + + +# ======================= Input/Output Codes ======================= +# CODE PARAMETERS DESCRIPTION +# M62 - M65 P Digital Output Control +# M66 P E L Q Wait on Input +# M67 T Analog Output, Synchronized +# M68 T Analog Output, Immediate + +class GCodeDigitalOutputOnSyncd(GCodeDigitalOutput): + """M62: turn on digital output synchronized with motion""" + word_key = Word('M', 62) + + +class GCodeDigitalOutputOffSyncd(GCodeDigitalOutput): + """M63: turn off digital output synchronized with motion""" + word_key = Word('M', 63) + + +class GCodeDigitalOutputOn(GCodeDigitalOutput): + """M64: turn on digital output immediately""" + word_key = Word('M', 64) + + +class GCodeDigitalOutputOff(GCodeDigitalOutput): + """M65: turn off digital output immediately""" + word_key = Word('M', 65) + + +class GCodeWaitOnInput(GCodeIO): + """M66: Wait on Input""" + param_letters = set('PELQ') + word_key = Word('M', 66) + + +class GCodeAnalogOutput(GCodeIO): + """Analog Output""" + param_letters = set('T') + + +class GCodeAnalogOutputSyncd(GCodeAnalogOutput): + """M67: Analog Output, Synchronized""" + word_key = Word('M', 67) + + +class GCodeAnalogOutputImmediate(GCodeAnalogOutput): + """M68: Analog Output, Immediate""" + word_key = Word('M', 68) + + +# ======================= Non-modal Codes ======================= +# CODE PARAMETERS DESCRIPTION +# M6 T Tool Change +# M61 Q Set Current Tool +# G10 L1 P Q R Set Tool Table +# G10 L10 P Set Tool Table +# G10 L11 P Set Tool Table +# G10 L2 P R ABCXYZ Set Coordinate System +# G10 L20 P ABCXYZ Set Coordinate System +# G28, G28.1 Go/Set Predefined Position +# G29 Marlin2: Bed Leveling +# G30 Marlin2: Single Z-Probe +# G30, G30.1 Go/Set Predefined Position +# G53 Move in Machine Coordinates +# G92 ABCXYZUVW Coordinate System Offset +# G92.1, G92.2 Reset G92 Offsets +# G92.3 Restore G92 Offsets +# M101 - M199 P Q User Defined Commands + +class GCodeToolChange(GCodeNonModal): + """M6: Tool Change""" + param_letters = set('T') + word_key = Word('M', 6) + word_letter = 'M' + exec_order = 80 + + +class GCodeToolSetCurrent(GCodeNonModal): + """M61: Set Current Tool""" + param_letters = set('Q') + word_key = Word('M', 61) + word_letter = 'M' + exec_order = 80 + +class GCodeSet(GCodeNonModal): + """G10: Set stuff""" + dialects = DIALECT_UNKNOWN + param_letters = set('LPQRABCXYZ') + word_key = Word('G', 10) + exec_order = 230 + + +class GCodeGotoPredefinedPosition(GCodeNonModal): + """G28,G30: Goto Predefined Position (rapid movement)""" + param_letters = set('W') + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [28, 30]) + default_word = Word('G', 28) + exec_order = 230 + + +class GCodeSetPredefinedPosition(GCodeNonModal): + """G28.1,G30.1: Set Predefined Position""" # redundancy in language there, but I'll let it slide + param_letters = set('W') + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [28.1, 30.1]) + default_word = Word('G', 28.1) + exec_order = 230 + +class GCodeMoveInMachineCoords(GCodeNonModal): + """G53: Move in Machine Coordinates""" + word_key = Word('G', 53) + exec_order = 240 + + +class GCodeCoordSystemOffset(GCodeNonModal): + """G92: Coordinate System Offset""" + param_letters = set('XYZABCUVW') + word_key = Word('G', 92) + exec_order = 230 + + +class GCodeResetCoordSystemOffset(GCodeNonModal): + """G92.1,G92.2: Reset Coordinate System Offset""" + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [92.1, 92.2]) + default_word = Word('G', 92.1) + exec_order = 230 + + # TODO: machine.state.offset *= 0 + + +class GCodeRestoreCoordSystemOffset(GCodeNonModal): + """G92.3: Restore Coordinate System Offset""" + word_key = Word('G', 92.3) + exec_order = 230 + + +class GCodeUserDefined(GCodeNonModal): + """M101-M199: User Defined Commands""" + word_letter = 'M' + # To create user g-codes, inherit from this class + param_letters = set('PQ') + #@classmethod + #def word_matches(cls, w): + # return (w.letter == 'M') and (101 <= w.value <= 199) + #default_word = Word('M', 101) + exec_order = 130 + modal_group = MODAL_GROUP_MAP['user_defined'] + diff --git a/src/pygcode/gcodes_marlin.py b/src/pygcode/gcodes_marlin.py new file mode 100644 index 0000000..6d3b65a --- /dev/null +++ b/src/pygcode/gcodes_marlin.py @@ -0,0 +1,1501 @@ +# DO NOT EDIT - file generated with src/pygcode/tools/marlin2_parse_MarlinDocumentation.py +from .gcodes_base import * + +class GCodeG0LinearMove(GCodeMotion): + """G0, G1: Add a straight line movement to the planner""" + param_letters = "XYZEFS" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [0, 1]) + default_word = Word('G', 0) + +class GCodeG2ArcOrCircleMove(GCodeArcMove): + """G2, G3: Add an arc or circle movement to the planner""" + param_letters = "XYZIJREFPS" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [2, 3]) + default_word = Word('G', 2) + +class GCodeG4Dwell(GCode): + """G4: Pause the planner""" + param_letters = "SP" + dialects = ['marlin2'] + word_key = Word('G', 4) + +class GCodeG5BzierCubicSpline(GCodeMotion): + """G5: Cubic B-spline with XYE destination and IJPQ offsets""" + param_letters = "XYEFIJPQS" + dialects = ['marlin2'] + word_key = Word('G', 5) + +class GCodeG6DirectStepperMove(GCodeMotion): + """G6: Perform a direct, uninterpolated, and non-kinematic synchronized move""" + param_letters = "IRSXYZE" + dialects = ['marlin2'] + word_key = Word('G', 6) + +class GCodeG10Retract(GCode): + """G10: Retract the filament""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('G', 10) + +class GCodeG11Recover(GCode): + """G11: Recover the filament with firmware-based retract.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 11) + +class GCodeG12CleanTheNozzle(GCodeMotion): + """G12: Perform the nozzle cleaning procedure.""" + param_letters = "PRSTXYZ" + dialects = ['marlin2'] + word_key = Word('G', 12) + +class GCodeG17CncWorkspacePlanes(GCodePlaneSelect): + """G17, G18, G19: Select CNC workspace plane""" + param_letters = "" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [17, 18, 19]) + default_word = Word('G', 17) + +class GCodeG20InchUnits(GCodeUnit): + """G20: Set Units to Inches.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 20) + +class GCodeG21MillimeterUnits(GCodeUnit): + """G21: Set Units to Millimeters.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 21) + +class GCodeG26MeshValidationPattern(GCodeMotion): + """G26: Test the mesh and adjust.""" + param_letters = "BCDFHIKLOPQRSUXY" + dialects = ['marlin2'] + word_key = Word('G', 26) + +class GCodeG27ParkToolhead(GCodeMotion): + """G27: Park the current toolhead""" + param_letters = "P" + dialects = ['marlin2'] + word_key = Word('G', 27) + +class GCodeG28AutoHome(GCodeMotion): + """G28: Auto home one or more axes.""" + param_letters = "LORXYZ" + dialects = ['marlin2'] + word_key = Word('G', 28) + +class GCodeG29BedLevelingPointg1(GCodeMotion): + """G29: Probe the bed and enable leveling compensation.""" + param_letters = "ACOQEDJV" + dialects = ['marlin2'] + word_key = Word('G', 29) + +class GCodeG29BedLevelingBilinearm2(GCodeMotion): + """G29: Probe the bed and enable leveling compensation.""" + param_letters = "ACOQXYZWSEDHFBLRJV" + dialects = ['marlin2'] + word_key = Word('G', 29) + +class GCodeG29BedLevelingLinearg2(GCodeMotion): + """G29: Probe the bed and enable leveling compensation.""" + param_letters = "ACOQXYPSEDTHFBLRJV" + dialects = ['marlin2'] + word_key = Word('G', 29) + +class GCodeG29BedLevelingManualm1(GCodeMotion): + """G29: Measure Z heights in a grid, enable leveling compensation""" + param_letters = "SIJXYZ" + dialects = ['marlin2'] + word_key = Word('G', 29) + +class GCodeG29BedLeveling(GCodeMotion): + """G29: Probe the bed and enable leveling compensation""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 29) + +class GCodeG29BedLevelingUnifiedm3(GCodeMotion): + """G29: Probe the bed and enable leveling compensation.""" + param_letters = "ABCDEFHIJKLPQRSTUVWXY" + dialects = ['marlin2'] + word_key = Word('G', 29) + +class GCodeG30SingleZProbe(GCodeMotion): + """G30: Probe bed at current XY location""" + param_letters = "CXYE" + dialects = ['marlin2'] + word_key = Word('G', 30) + +class GCodeG31DockSled(GCodeMotion): + """G31: Dock the Z probe sled.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 31) + +class GCodeG32UndockSled(GCodeMotion): + """G32: Undock the Z probe sled.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 32) + +class GCodeG33DeltaAutoCalibration(GCodeMotion): + """G33: Calibrate various Delta parameters""" + param_letters = "CEFPTVOR" + dialects = ['marlin2'] + word_key = Word('G', 33) + +class GCodeG34MechanicalGantryCalibrationb(GCodeMotion): + """G34: Modern replacement for Průša's TMC_Z_CALIBRATION""" + param_letters = "SZ" + dialects = ['marlin2'] + word_key = Word('G', 34) + +class GCodeG34ZSteppersAutoAlignmenta(GCodeMotion): + """G34: Align multiple Z steppers using a bed probe""" + param_letters = "ITAE" + dialects = ['marlin2'] + word_key = Word('G', 34) + +class GCodeG35TrammingAssistant(GCodeMotion): + """G35: Run a procedure to tram the bed""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('G', 35) + +class GCodeG38ProbeTarget(GCodeMotion): + """G38.2, G38.3, G38.4, G38.5: Probe towards (or away from) a workpiece""" + param_letters = "XYZF" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [38.2, 38.3, 38.4, 38.5]) + default_word = Word('G', 38) + +class GCodeG42MoveToMeshCoordinate(GCodeSelectCoordinateSystem): + """G42: Move to a specific point in the leveling mesh""" + param_letters = "IJF" + dialects = ['marlin2'] + word_key = Word('G', 42) + +class GCodeG53MoveInMachineCoordinates(GCodeSelectCoordinateSystem): + """G53: Apply native workspace to the current move.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 53) + +class GCodeG54WorkspaceCoordinateSystem(GCodeSelectCoordinateSystem): + """G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3: Select a workspace coordinate system""" + param_letters = "" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'G') and (w.value in [54, 55, 56, 57, 58, 59, 59.1, 59.2, 59.3]) + default_word = Word('G', 54) + +class GCodeG60SaveCurrentPosition(GCode): + """G60: Save current position to specified slot""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('G', 60) + +class GCodeG61ReturnToSavedPosition(GCodeMotion): + """G61: Return to saved position of specified slot""" + param_letters = "FSXYZE" + dialects = ['marlin2'] + word_key = Word('G', 61) + +class GCodeG76ProbeTemperatureCalibration(GCodeOtherModal): + """G76: Calibrate probe temperature compensation""" + param_letters = "BP" + dialects = ['marlin2'] + word_key = Word('G', 76) + +class GCodeG80CancelCurrentMotionMode(GCode): + """G80: Cancel the current motion mode""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 80) + +class GCodeG90AbsolutePositioning(GCodeOtherModal): + """G90: Set the interpreter to absolute positions""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 90) + +class GCodeG91RelativePositioning(GCodeOtherModal): + """G91: Set the interpreter to relative positions""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('G', 91) + +class GCodeG92SetPosition(GCodeOtherModal): + """G92: Set the current position of one or more axes.""" + param_letters = "XYZE" + dialects = ['marlin2'] + word_key = Word('G', 92) + +class GCodeG425BacklashCalibration(GCodeOtherModal): + """G425: Use a conductive object to calibrate XYZ backlash""" + param_letters = "BTVU" + dialects = ['marlin2'] + word_key = Word('G', 425) + +class GCodeM0UnconditionalStop(GCodeOtherModal): + """M0, M1: Stop and wait for user""" + param_letters = "SPg" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'M') and (w.value in [0, 1]) + default_word = Word('M', 0) + +class GCodeM3SpindleCwLaserOn(GCodeSpindle): + """M3: Set the spindle CW speed or laser power""" + param_letters = "SOI" + dialects = ['marlin2'] + word_key = Word('M', 3) + +class GCodeM4SpindleCcwLaserOn(GCodeSpindle): + """M4: Set the spindle CCW speed or laser power""" + param_letters = "SOI" + dialects = ['marlin2'] + word_key = Word('M', 4) + +class GCodeM5SpindleLaserOff(GCodeSpindle): + """M5: Turn off spindle or laser""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 5) + +class GCodeM7CoolantControls(GCodeCoolant): + """M7, M8, M9: Turn mist or flood coolant on / off""" + param_letters = "" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'M') and (w.value in [7, 8, 9]) + default_word = Word('M', 7) + +class GCodeM10VacuumBlowerControl(GCodeDigitalOutput): + """M10, M11: Enable and disable the Cutter Vacuum or Laser Blower Motor.""" + param_letters = "" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'M') and (w.value in [10, 11]) + default_word = Word('M', 10) + +class GCodeM16ExpectedPrinterCheck(GCodeOtherModal): + """M16: Prevent G-code usage on the wrong machine""" + param_letters = "g" + dialects = ['marlin2'] + word_key = Word('M', 16) + +class GCodeM17EnableSteppers(GCodeOtherModal): + """M17: Enable steppers""" + param_letters = "'" + dialects = ['marlin2'] + word_key = Word('M', 17) + +class GCodeM18DisableSteppers(GCodeOtherModal): + """M18, M84: Disable steppers (same as M84).""" + param_letters = "S'" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'M') and (w.value in [18, 84]) + default_word = Word('M', 18) + +class GCodeM20ListSdCard(GCodeIO): + """M20: List the contents of the SD Card.""" + param_letters = "FLT" + dialects = ['marlin2'] + word_key = Word('M', 20) + +class GCodeM21InitSdCard(GCodeIO): + """M21: Attempt to detect an SD card in the slot.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 21) + +class GCodeM22ReleaseSdCard(GCodeIO): + """M22: Simulate ejection of the SD card""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 22) + +class GCodeM23SelectSdFile(GCodeIO): + """M23: Select an SD file to be executed""" + param_letters = "e" + dialects = ['marlin2'] + word_key = Word('M', 23) + +class GCodeM24StartOrResumeSdPrint(GCodeOtherModal): + """M24: Start or resume a file selected with M23""" + param_letters = "ST" + dialects = ['marlin2'] + word_key = Word('M', 24) + +class GCodeM25PauseSdPrint(GCodeOtherModal): + """M25: Pause printing from the SD card""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 25) + +class GCodeM26SetSdPosition(GCodeIO): + """M26: Set the SD read position""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 26) + +class GCodeM27ReportSdPrintStatus(GCodeNonModal): + """M27: Print SD progress to serial""" + param_letters = "SC" + dialects = ['marlin2'] + word_key = Word('M', 27) + +class GCodeM28StartSdWrite(GCodeIO): + """M28: Start writing to a file on the SD card""" + param_letters = "1e" + dialects = ['marlin2'] + word_key = Word('M', 28) + +class GCodeM29StopSdWrite(GCodeIO): + """M29: Stop writing the file, end logging.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 29) + +class GCodeM30DeleteSdFile(GCodeIO): + """M30: Delete a specified file from SD.""" + param_letters = "e" + dialects = ['marlin2'] + word_key = Word('M', 30) + +class GCodeM31PrintTime(GCode): + """M31: Report the current print time.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 31) + +class GCodeM32SelectAndStart(GCodeIO): + """M32: Begin an SD print from a file.""" + param_letters = "PS" + dialects = ['marlin2'] + word_key = Word('M', 32) + +class GCodeM33GetLongPath(GCodeIO): + """M33: Convert a short pathname to a long pathname.""" + param_letters = "h" + dialects = ['marlin2'] + word_key = Word('M', 33) + +class GCodeM34SdcardSorting(GCodeIO): + """M34: Set SDCard file sorting options.""" + param_letters = "SF" + dialects = ['marlin2'] + word_key = Word('M', 34) + +class GCodeM42SetPinState(GCodeDigitalOutput): + """M42: Set an analog or digital pin to a specified state.""" + param_letters = "ITPS" + dialects = ['marlin2'] + word_key = Word('M', 42) + +class GCodeM43DebugPins(GCodeIO): + """M43: Get information about pins.""" + param_letters = "PWETSI" + dialects = ['marlin2'] + word_key = Word('M', 43) + +class GCodeM43TogglePinsb(GCodeDigitalOutput): + """M43T: Get information about pins.""" + param_letters = "SLIRW" + dialects = ['marlin2'] + word_key = Word('M', 43) + +class GCodeM48ProbeRepeatabilityTest(GCodeMotion): + """M48: Measure Z Probe repeatability.""" + param_letters = "CELPSVXY" + dialects = ['marlin2'] + word_key = Word('M', 48) + +class GCodeM73SetPrintProgress(GCode): + """M73: Set current print progress percentage for LCD.""" + param_letters = "PR" + dialects = ['marlin2'] + word_key = Word('M', 73) + +class GCodeM75StartPrintJobTimer(GCodeOtherModal): + """M75: Start the print job timer.""" + param_letters = "g" + dialects = ['marlin2'] + word_key = Word('M', 75) + +class GCodeM76PausePrintJobTimer(GCodeOtherModal): + """M76: Pause the print job timer.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 76) + +class GCodeM77StopPrintJobTimer(GCodeOtherModal): + """M77: Stop the print job timer.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 77) + +class GCodeM78PrintJobStats(GCode): + """M78: Print statistics about print jobs.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 78) + +class GCodeM80PowerOn(GCodeOtherModal): + """M80: Turn on the power supply""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 80) + +class GCodeM81PowerOff(GCodeOtherModal): + """M81: Turn off the power supply.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 81) + +class GCodeM82EAbsolute(GCodeOtherModal): + """M82: Set E to absolute positioning.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 82) + +class GCodeM83ERelative(GCodeOtherModal): + """M83: Set E to relative positioning.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 83) + +class GCodeM85InactivityShutdown(GCodeOtherModal): + """M85: Set the inactivity timeout.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 85) + +class GCodeM86HotendIdleTimeout(GCodeOtherModal): + """M86: Set the hotend idle timeout.""" + param_letters = "STEB" + dialects = ['marlin2'] + word_key = Word('M', 86) + +class GCodeM87DisableHotendIdleTimeout(GCodeOtherModal): + """M87: Disable the hotend idle timeout.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 87) + +class GCodeM92SetAxisStepsPerUnit(GCodeUnit): + """M92: Set the number of steps-per-mm, per-inch, or per-degree""" + param_letters = "XYZET" + dialects = ['marlin2'] + word_key = Word('M', 92) + +class GCodeM100FreeMemory(GCodeNonModal): + """M100: Observe memory used by code""" + param_letters = "DFIC" + dialects = ['marlin2'] + word_key = Word('M', 100) + +class GCodeM102ConfigureBedDistanceSensor(GCodeOtherModal): + """M102: Set parameters for the Bed Distance Sensor.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 102) + +class GCodeM104SetHotendTemperature(GCodeOtherModal): + """M104: Set a new target hot end temperature.""" + param_letters = "ISFBT" + dialects = ['marlin2'] + word_key = Word('M', 104) + +class GCodeM105ReportTemperatures(GCodeNonModal): + """M105: Send a temperature report to the host.""" + param_letters = "RT" + dialects = ['marlin2'] + word_key = Word('M', 105) + +class GCodeM106SetFanSpeed(GCodeDigitalOutput): + """M106: Turn on fan and set speed""" + param_letters = "ISPT" + dialects = ['marlin2'] + word_key = Word('M', 106) + +class GCodeM107FanOff(GCodeDigitalOutput): + """M107: Turn off a fan""" + param_letters = "P" + dialects = ['marlin2'] + word_key = Word('M', 107) + +class GCodeM108BreakAndContinue(GCodeOtherModal): + """M108: Break out of the current waiting loop""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 108) + +class GCodeM109WaitForHotendTemperature(GCode): + """M109: Wait for the hot end to reach its target.""" + param_letters = "ISRFBT" + dialects = ['marlin2'] + word_key = Word('M', 109) + +class GCodeM110SetLineNumber(GCodeOtherModal): + """M110: Set the current line number.""" + param_letters = "N" + dialects = ['marlin2'] + word_key = Word('M', 110) + +class GCodeM111DebugLevel(GCodeOtherModal): + """M111: Report and optionally set the debug flags.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 111) + +class GCodeM112FullShutdown(GCodeOtherModal): + """M112: Shut everything down and halt the machine.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 112) + +class GCodeM113HostKeepalive(GCode): + """M113: Get or set the host keepalive interval.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 113) + +class GCodeM114GetCurrentPosition(GCodeNonModal): + """M114: Report the current tool position to the host.""" + param_letters = "DER" + dialects = ['marlin2'] + word_key = Word('M', 114) + +class GCodeM115FirmwareInfo(GCodeNonModal): + """M115: Print the firmware info and capabilities.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 115) + +class GCodeM117SetLcdMessage(GCodeIO): + """M117: Set the message line on the LCD.""" + param_letters = "g" + dialects = ['marlin2'] + word_key = Word('M', 117) + +class GCodeM118SerialPrint(GCodeIO): + """M118: Send text to serial""" + param_letters = "g11n" + dialects = ['marlin2'] + word_key = Word('M', 118) + +class GCodeM119EndstopStates(GCodeIO): + """M119: Report endstop and probe states to the host.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 119) + +class GCodeM120EnableEndstops(GCodeIO): + """M120: Enable endstops and keep them enabled even when not homing.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 120) + +class GCodeM121DisableEndstops(GCodeIO): + """M121: Disable endstops and keep them disabled when not homing.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 121) + +class GCodeM122TmcDebugging(GCodeIO): + """M122: Get TMC Debug Info""" + param_letters = "IXYZEVSP" + dialects = ['marlin2'] + word_key = Word('M', 122) + +class GCodeM123FanTachometers(GCodeIO): + """M123: Report fan speeds from tachometers""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 123) + +class GCodeM125ParkHead(GCodeMotion): + """M125: Save current position and move to filament change position.""" + param_letters = "LXYZP" + dialects = ['marlin2'] + word_key = Word('M', 125) + +class GCodeM126BaricudaOpen(GCodeDigitalOutput): + """M126: Open the valve for Baricuda 1.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 126) + +class GCodeM127BaricudaClose(GCodeDigitalOutput): + """M127: Close the valve for Baricuda 1.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 127) + +class GCodeM128BaricudaOpen(GCodeDigitalOutput): + """M128: Open the valve for Baricuda 2.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 128) + +class GCodeM129BaricudaClose(GCodeDigitalOutput): + """M129: Close the valve for Baricuda 2.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 129) + +class GCodeM140SetBedTemperature(GCodeOtherModal): + """M140: Set a new target bed temperature.""" + param_letters = "IS" + dialects = ['marlin2'] + word_key = Word('M', 140) + +class GCodeM141SetChamberTemperature(GCodeOtherModal): + """M141: Set a new target chamber temperature.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 141) + +class GCodeM143SetLaserCoolerTemperature(GCodeCoolant): + """M143: Set a new target laser coolant temperature.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 143) + +class GCodeM145SetMaterialPreset(GCodeOtherModal): + """M145: Set material presets in the LCD menu.""" + param_letters = "SHBF" + dialects = ['marlin2'] + word_key = Word('M', 145) + +class GCodeM149SetTemperatureUnits(GCodeUnit): + """M149: Set temperature units to Celsius, Fahrenheit, or Kelvin.""" + param_letters = "CFK" + dialects = ['marlin2'] + word_key = Word('M', 149) + +class GCodeM150SetRgbWColor(GCodeDigitalOutput): + """M150: Set the color of the RGB(W) LED, backlight, or LED strip.""" + param_letters = "RUBWPISK" + dialects = ['marlin2'] + word_key = Word('M', 150) + +class GCodeM154PositionAutoReport(GCodeNonModal): + """M154: Periodically auto-report position to serial""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 154) + +class GCodeM155TemperatureAutoReport(GCodeNonModal): + """M155: Auto-report temperatures to host periodically.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 155) + +class GCodeM163SetMixFactor(GCodeOtherModal): + """M163: Set a single mix factor for a mixing extruder.""" + param_letters = "SP" + dialects = ['marlin2'] + word_key = Word('M', 163) + +class GCodeM164SaveMix(GCodeOtherModal): + """M164: Save the current mix as a virtual tool.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 164) + +class GCodeM165SetMix(GCodeOtherModal): + """M165: Set all mix factors for the mixing extruder.""" + param_letters = "ABCDHI" + dialects = ['marlin2'] + word_key = Word('M', 165) + +class GCodeM166GradientMix(GCodeOtherModal): + """M166: Set a Gradient Mix""" + param_letters = "AZIJST" + dialects = ['marlin2'] + word_key = Word('M', 166) + +class GCodeM190WaitForBedTemperature(GCode): + """M190: Wait for the bed to reach target temperature.""" + param_letters = "ISRT" + dialects = ['marlin2'] + word_key = Word('M', 190) + +class GCodeM191WaitForChamberTemperature(GCode): + """M191: Wait for the chamber to reach target temperature.""" + param_letters = "SR" + dialects = ['marlin2'] + word_key = Word('M', 191) + +class GCodeM192WaitForProbeTemperature(GCode): + """M192: Wait for the probe temperature sensor to reach a target""" + param_letters = "RS" + dialects = ['marlin2'] + word_key = Word('M', 192) + +class GCodeM193SetLaserCoolerTemperature(GCodeCoolant): + """M193: Set a new target laser coolant temperature.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 193) + +class GCodeM200SetFilamentDiameter(GCodeOtherModal): + """M200: Set the diameter for volumetric extrusion.""" + param_letters = "DLST" + dialects = ['marlin2'] + word_key = Word('M', 200) + +class GCodeM201PrintTravelMoveLimits(GCodeNonModal): + """M201: Set acceleration / frequency limits for print and travel moves.""" + param_letters = "XYZETFS" + dialects = ['marlin2'] + word_key = Word('M', 201) + +class GCodeM203SetMaxFeedrate(GCodeFeedRateMode): + """M203: Set maximum feedrate for one or more axes.""" + param_letters = "XYZET" + dialects = ['marlin2'] + word_key = Word('M', 203) + +class GCodeM204SetStartingAcceleration(GCodeOtherModal): + """M204: Set the starting acceleration for moves by type.""" + param_letters = "PRTS" + dialects = ['marlin2'] + word_key = Word('M', 204) + +class GCodeM205SetAdvancedSettings(GCodeOtherModal): + """M205: Set some advanced settings related to movement.""" + param_letters = "XYZEBSTJ" + dialects = ['marlin2'] + word_key = Word('M', 205) + +class GCodeM206SetHomeOffsets(GCodeOtherModal): + """M206: Apply a persistent offset""" + param_letters = "PTXYZ" + dialects = ['marlin2'] + word_key = Word('M', 206) + +class GCodeM207SetFirmwareRetraction(GCodeOtherModal): + """M207: Set options for firmware-based retraction.""" + param_letters = "SWFZ" + dialects = ['marlin2'] + word_key = Word('M', 207) + +class GCodeM208FirmwareRecover(GCodeOtherModal): + """M208: Firmware-retraction recover settings.""" + param_letters = "SWFR" + dialects = ['marlin2'] + word_key = Word('M', 208) + +class GCodeM209SetAutoRetract(GCodeOtherModal): + """M209: Enable / disable auto-retraction.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 209) + +class GCodeM211SoftwareEndstops(GCodeOtherModal): + """M211: Set and/or get the software endstops state""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 211) + +class GCodeM217FilamentSwapParameters(GCodeOtherModal): + """M217: Set length and speed for filament swapping""" + param_letters = "QSBEPRUFGALWXYVZ" + dialects = ['marlin2'] + word_key = Word('M', 217) + +class GCodeM218SetHotendOffset(GCodeOtherModal): + """M218: Set the offset of a hotend (from hotend 0).""" + param_letters = "TXYZ" + dialects = ['marlin2'] + word_key = Word('M', 218) + +class GCodeM220SetFeedratePercentage(GCodeFeedRateMode): + """M220: Set the global feedrate percentage.""" + param_letters = "SBR" + dialects = ['marlin2'] + word_key = Word('M', 220) + +class GCodeM221SetFlowPercentage(GCodeOtherModal): + """M221: Set the flow percentage, which applies to all E moves.""" + param_letters = "ST" + dialects = ['marlin2'] + word_key = Word('M', 221) + +class GCodeM226WaitForPinState(GCodeIO): + """M226: Wait for a pin to have a given state.""" + param_letters = "PS" + dialects = ['marlin2'] + word_key = Word('M', 226) + +class GCodeM240TriggerCamera(GCodeIO): + """M240: Trigger a camera shutter""" + param_letters = "ABDFIJPRSXYZ" + dialects = ['marlin2'] + word_key = Word('M', 240) + +class GCodeM250LcdContrast(GCodeIO): + """M250: Set and/or get the LCD contrast.""" + param_letters = "C" + dialects = ['marlin2'] + word_key = Word('M', 250) + +class GCodeM255LcdSleepBacklightTimeout(GCodeIO): + """M255: Set and/or get the LCD Sleep Timeout.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 255) + +class GCodeM256LcdBrightness(GCodeIO): + """M256: Set and/or get the LCD brightness.""" + param_letters = "B" + dialects = ['marlin2'] + word_key = Word('M', 256) + +class GCodeM260ICSend(GCodeDigitalOutput): + """M260: Send data to the I2C bus.""" + param_letters = "ABRS" + dialects = ['marlin2'] + word_key = Word('M', 260) + +class GCodeM261ICRequest(GCodeIO): + """M261: Request and echo bytes from the I2C bus.""" + param_letters = "ABS" + dialects = ['marlin2'] + word_key = Word('M', 261) + +class GCodeM280ServoPosition(GCodeIO): + """M280: Set or get a servo position.""" + param_letters = "PS" + dialects = ['marlin2'] + word_key = Word('M', 280) + +class GCodeM281EditServoAngles(GCodeOtherModal): + """M281: Set servo deploy and/or stow angles""" + param_letters = "PLU" + dialects = ['marlin2'] + word_key = Word('M', 281) + +class GCodeM282DetachServo(GCodeOtherModal): + """M282: Detach a servo until its next move""" + param_letters = "P" + dialects = ['marlin2'] + word_key = Word('M', 282) + +class GCodeM290Babystep(GCodeMotion): + """M290: Babystep one or more axes""" + param_letters = "XYZSP" + dialects = ['marlin2'] + word_key = Word('M', 290) + +class GCodeM300PlayTone(GCodeDigitalOutput): + """M300: Play a single tone, buzz, or beep.""" + param_letters = "PS" + dialects = ['marlin2'] + word_key = Word('M', 300) + +class GCodeM301SetHotendPid(GCodeOtherModal): + """M301: Set PID values for a hotend.""" + param_letters = "EPIDCLF" + dialects = ['marlin2'] + word_key = Word('M', 301) + +class GCodeM302ColdExtrude(GCodeOtherModal): + """M302: Set minimum extrusion temperature, allow cold extrusion.""" + param_letters = "SP" + dialects = ['marlin2'] + word_key = Word('M', 302) + +class GCodeM303PidAutotune(GCodeOtherModal): + """M303: Auto-tune the PID system to find stable values.""" + param_letters = "ECSUD" + dialects = ['marlin2'] + word_key = Word('M', 303) + +class GCodeM304SetBedPid(GCodeOtherModal): + """M304: Set PID values for the heated bed.""" + param_letters = "PID" + dialects = ['marlin2'] + word_key = Word('M', 304) + +class GCodeM305UserThermistorParameters(GCodeOtherModal): + """M305: Set (or report) custom thermistor parameters""" + param_letters = "PRTBC" + dialects = ['marlin2'] + word_key = Word('M', 305) + +class GCodeM306ModelPredictiveTempControl(GCodeOtherModal): + """M306: Set MPC values for a hotend.""" + param_letters = "ACEFHPRST" + dialects = ['marlin2'] + word_key = Word('M', 306) + +class GCodeM350SetMicroStepping(GCodeOtherModal): + """M350: Set micro-stepping for drivers that support it""" + param_letters = "BSXYZE" + dialects = ['marlin2'] + word_key = Word('M', 350) + +class GCodeM351SetMicrostepPins(GCodeOtherModal): + """M351: Directly set the micro-stepping pins""" + param_letters = "SBXYZE" + dialects = ['marlin2'] + word_key = Word('M', 351) + +class GCodeM355CaseLightControl(GCodeDigitalOutput): + """M355: Turn the case light on or off, set brightness""" + param_letters = "PS" + dialects = ['marlin2'] + word_key = Word('M', 355) + +class GCodeM360ScaraThetaA(GCodeMotion): + """M360: Move to Theta A""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 360) + +class GCodeM361ScaraThetaB(GCodeMotion): + """M361: Move to Theta-B""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 361) + +class GCodeM362ScaraPsiA(GCodeMotion): + """M362: Move to Psi-A""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 362) + +class GCodeM363ScaraPsiB(GCodeMotion): + """M363: Move to Psi-B""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 363) + +class GCodeM364ScaraPsiC(GCodeMotion): + """M364: Move to Psi-C""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 364) + +class GCodeM380ActivateSolenoid(GCodeDigitalOutput): + """M380: Activate""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 380) + +class GCodeM381DeactivateSolenoids(GCodeDigitalOutput): + """M381: Deactivate all extruder solenoids""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 381) + +class GCodeM400FinishMoves(GCodeOtherModal): + """M400: Wait for all moves to finish""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 400) + +class GCodeM401DeployProbe(GCodeDigitalOutput): + """M401: Deploy the bed probe""" + param_letters = "HS" + dialects = ['marlin2'] + word_key = Word('M', 401) + +class GCodeM402StowProbe(GCodeDigitalOutput): + """M402: Stow the bed probe""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 402) + +class GCodeM403MmuFilamentType(GCodeOtherModal): + """M403: Set filament type for Multi-Material Unit 2.0""" + param_letters = "EF" + dialects = ['marlin2'] + word_key = Word('M', 403) + +class GCodeM404SetFilamentDiameter(GCodeOtherModal): + """M404: Set the nominal diameter for filament width sensor auto-flow""" + param_letters = "W" + dialects = ['marlin2'] + word_key = Word('M', 404) + +class GCodeM405FilamentWidthSensorOn(GCodeOtherModal): + """M405: Enable filament width sensor flow control""" + param_letters = "D" + dialects = ['marlin2'] + word_key = Word('M', 405) + +class GCodeM406FilamentWidthSensorOff(GCodeOtherModal): + """M406: Disable filament width sensor flow control""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 406) + +class GCodeM407FilamentWidth(GCodeNonModal): + """M407: Report the measured filament width""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 407) + +class GCodeM410Quickstop(GCodeOtherModal): + """M410: Stop all steppers instantly""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 410) + +class GCodeM412FilamentRunout(GCodeOtherModal): + """M412: Get/set filament runout detection parameters""" + param_letters = "DHSR" + dialects = ['marlin2'] + word_key = Word('M', 412) + +class GCodeM413PowerLossRecovery(GCodeOtherModal): + """M413: Enable / disable power-loss recovery""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 413) + +class GCodeM420BedLevelingState(GCodeNonModal): + """M420: Get and/or set bed leveling state and parameters""" + param_letters = "LSVTZC" + dialects = ['marlin2'] + word_key = Word('M', 420) + +class GCodeM421SetMeshValue(GCodeOtherModal): + """M421: Set a single mesh Z height""" + param_letters = "IJXYZQCN" + dialects = ['marlin2'] + word_key = Word('M', 421) + +class GCodeM422SetZMotorXy(GCodeOtherModal): + """M422: Set a Z motor position for G34 Auto-Alignment""" + param_letters = "RSWXY" + dialects = ['marlin2'] + word_key = Word('M', 422) + +class GCodeM423XTwistCompensation(GCodeOtherModal): + """M423: Modify, reset, and report X-Axis Twist Compensation data""" + param_letters = "RAIXZ" + dialects = ['marlin2'] + word_key = Word('M', 423) + +class GCodeM425BacklashCompensation(GCodeOtherModal): + """M425: Enable and tune backlash compensation""" + param_letters = "FSXYZZ" + dialects = ['marlin2'] + word_key = Word('M', 425) + +class GCodeM428HomeOffsetsHere(GCodeOtherModal): + """M428: Set home offsets based on current position""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 428) + +class GCodeM430PowerMonitor(GCodeOtherModal): + """M430: Read and display current (A), voltage (V), and power (W)""" + param_letters = "IVW" + dialects = ['marlin2'] + word_key = Word('M', 430) + +class GCodeM486CancelObjects(GCodeOtherModal): + """M486: Identify and cancel objects""" + param_letters = "CPSTU" + dialects = ['marlin2'] + word_key = Word('M', 486) + +class GCodeM493FixedTimeMotion(GCodeOtherModal): + """M493: Enable/disable and configure Fixed-Time Motion, Linear Advance, and Input Shaping""" + param_letters = "SPKDABFH" + dialects = ['marlin2'] + word_key = Word('M', 493) + +class GCodeM500SaveSettings(GCodeNonModal): + """M500: Save settings to EEPROM.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 500) + +class GCodeM501RestoreSettings(GCodeOtherModal): + """M501: Restore settings from EEPROM.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 501) + +class GCodeM502FactoryReset(GCodeOtherModal): + """M502: Restore all settings to factory defaults.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 502) + +class GCodeM503ReportSettings(GCodeNonModal): + """M503: Report all settings that may be saved to EEPROM.""" + param_letters = "SC" + dialects = ['marlin2'] + word_key = Word('M', 503) + +class GCodeM504ValidateEepromContents(GCodeNonModal): + """M504: Validate the contents of the EEPROM.""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 504) + +class GCodeM510LockMachine(GCodeOtherModal): + """M510: Lock the machine if it has a passcode""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 510) + +class GCodeM511UnlockMachine(GCodeOtherModal): + """M511: Unlock the machine if it has a passcode""" + param_letters = "P" + dialects = ['marlin2'] + word_key = Word('M', 511) + +class GCodeM512SetPasscode(GCodeNonModal): + """M512: Set a numeric passcode for locking the machine""" + param_letters = "PS" + dialects = ['marlin2'] + word_key = Word('M', 512) + +class GCodeM524AbortSdPrint(GCodeOtherModal): + """M524: Abort an SD print started with [`M24`](/docs/gcode/M024.html)""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 524) + +class GCodeM540EndstopsAbortSd(GCodeNonModal): + """M540: Abort SD printing when an endstop is triggered.""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 540) + +class GCodeM569SetTmcSteppingMode(GCodeOtherModal): + """M569: Toggle stealthChop""" + param_letters = "XYZEIT" + dialects = ['marlin2'] + word_key = Word('M', 569) + +class GCodeM575SerialBaudRate(GCodeOtherModal): + """M575: Change the serial baud rate""" + param_letters = "PB" + dialects = ['marlin2'] + word_key = Word('M', 575) + +class GCodeM592NonlinearExtrusionControl(GCodeOtherModal): + """M592: Get or set Nonlinear Extrusion parameters""" + param_letters = "ABC" + dialects = ['marlin2'] + word_key = Word('M', 592) + +class GCodeM593ZvInputShaping(GCodeOtherModal): + """M593: Get or set Marlin's integrated ZV Input Shaping parameters""" + param_letters = "DFXY" + dialects = ['marlin2'] + word_key = Word('M', 593) + +class GCodeM600FilamentChange(GCode): + """M600: Automatically change filament""" + param_letters = "TEULXYZBR" + dialects = ['marlin2'] + word_key = Word('M', 600) + +class GCodeM603ConfigureFilamentChange(GCodeOtherModal): + """M603: Configure automatic filament change parameters""" + param_letters = "TUL" + dialects = ['marlin2'] + word_key = Word('M', 603) + +class GCodeM605MultiNozzleMode(GCodeOtherModal): + """M605: Set the behavior mode for a multiple nozzle setup""" + param_letters = "SXRPE" + dialects = ['marlin2'] + word_key = Word('M', 605) + +class GCodeM665DeltaConfiguration(GCodeOtherModal): + """M665: Set delta geometry values""" + param_letters = "HLRSXYZABC" + dialects = ['marlin2'] + word_key = Word('M', 665) + +class GCodeM665ScaraConfigurationb(GCodeOtherModal): + """M665: Set SCARA geometry values""" + param_letters = "SPTAXBY" + dialects = ['marlin2'] + word_key = Word('M', 665) + +class GCodeM666SetDualEndstopOffsetsb(GCodeOtherModal): + """M666: Set dual endstop offsets""" + param_letters = "XYZ" + dialects = ['marlin2'] + word_key = Word('M', 666) + +class GCodeM666SetDeltaEndstopAdjustmentsa(GCodeOtherModal): + """M666: Set Delta endstop adjustments""" + param_letters = "XYZ" + dialects = ['marlin2'] + word_key = Word('M', 666) + +class GCodeM672DuetSmartEffectorSensitivity(GCodeOtherModal): + """M672: Set Duet Smart Effector sensitivity""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 672) + +class GCodeM701LoadFilament(GCodeOtherModal): + """M701: Load filament""" + param_letters = "TZL" + dialects = ['marlin2'] + word_key = Word('M', 701) + +class GCodeM702UnloadFilament(GCodeOtherModal): + """M702: Unload filament""" + param_letters = "TZU" + dialects = ['marlin2'] + word_key = Word('M', 702) + +class GCodeM710ControllerFanSettings(GCodeOtherModal): + """M710: Set or report controller fan settings""" + param_letters = "SIARD" + dialects = ['marlin2'] + word_key = Word('M', 710) + +class GCodeM7219MaxControl(GCodeDigitalOutput): + """M7219: Control Max7219 Segmented LEDs""" + param_letters = "CDRIFPUVXY" + dialects = ['marlin2'] + word_key = Word('M', 7219) + +class GCodeM808RepeatMarker(GCodeOtherModal): + """M808: Set or go to a marker for looping G-code""" + param_letters = "L" + dialects = ['marlin2'] + word_key = Word('M', 808) + +class GCodeM810GCodeMacros(GCodeOtherModal): + """M810, M811, M812, M813, M814, M815, M816, M817, M818, M819: Set/execute one of ten G-code macros""" + param_letters = "g" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'M') and (w.value in [810, 811, 812, 813, 814, 815, 816, 817, 818, 819]) + default_word = Word('M', 810) + +class GCodeM851XyzProbeOffset(GCodeOtherModal): + """M851: Set the Z probe XYZ offset from nozzle""" + param_letters = "XYZ" + dialects = ['marlin2'] + word_key = Word('M', 851) + +class GCodeM852BedSkewCompensation(GCodeOtherModal): + """M852: Misalignment in the XYZ axes.""" + param_letters = "IJKS" + dialects = ['marlin2'] + word_key = Word('M', 852) + +class GCodeM860ICPositionEncoders(GCodeOtherModal): + """M860, M861, M862, M863, M864, M865, M866, M867, M868, M869: I2C position encoders for closed loop control""" + param_letters = "IOXYZEUPSRST" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'M') and (w.value in [860, 861, 862, 863, 864, 865, 866, 867, 868, 869]) + default_word = Word('M', 860) + +class GCodeM871ProbeTemperatureConfig(GCodeOtherModal): + """M871: Configure probe temperature compensation""" + param_letters = "VIBPER" + dialects = ['marlin2'] + word_key = Word('M', 871) + +class GCodeM876HandlePromptResponse(GCode): + """M876: Handle Host prompt responses""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 876) + +class GCodeM900LinearAdvanceFactor(GCodeOtherModal): + """M900: Get and set Linear Advance K value""" + param_letters = "KLST" + dialects = ['marlin2'] + word_key = Word('M', 900) + +class GCodeM906StepperMotorCurrent(GCodeOtherModal): + """M906: Set the motor current (in milliamps)""" + param_letters = "EITXYZ" + dialects = ['marlin2'] + word_key = Word('M', 906) + +class GCodeM907SetMotorCurrent(GCodeOtherModal): + """M907: Set motor current via digital trimpot""" + param_letters = "BCDESXYZ" + dialects = ['marlin2'] + word_key = Word('M', 907) + +class GCodeM908SetTrimpotPins(GCodeOtherModal): + """M908: Set a digital trimpot directly""" + param_letters = "PS" + dialects = ['marlin2'] + word_key = Word('M', 908) + +class GCodeM909DacPrintValues(GCodeNonModal): + """M909: Report DAC current values to host""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 909) + +class GCodeM910CommitDacToEeprom(GCodeNonModal): + """M910: Commit digipot/DAC value to external EEPROM""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 910) + +class GCodeM911TmcOtPreWarnCondition(GCode): + """M911: Driver overtemperature pre-warn condition""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 911) + +class GCodeM912ClearTmcOtPreWarn(GCode): + """M912: Clear overtemperature pre-warn condition flag""" + param_letters = "IXYZE" + dialects = ['marlin2'] + word_key = Word('M', 912) + +class GCodeM913SetHybridThresholdSpeed(GCodeOtherModal): + """M913: TMC driver switching to spreadCycle""" + param_letters = "ITXYZE" + dialects = ['marlin2'] + word_key = Word('M', 913) + +class GCodeM914TmcBumpSensitivity(GCodeOtherModal): + """M914: Set sensorless homing sensitivity""" + param_letters = "IXYZ" + dialects = ['marlin2'] + word_key = Word('M', 914) + +class GCodeM915TmcZAxisCalibration(GCode): + """M915: Align ends of the Z axis and test torque""" + param_letters = "SZ" + dialects = ['marlin2'] + word_key = Word('M', 915) + +class GCodeM916LThermalWarningTest(GCode): + """M916: Find L6474 drive level (KVAL_HOLD) threshold""" + param_letters = "JXYZEFTKD" + dialects = ['marlin2'] + word_key = Word('M', 916) + +class GCodeM917LOvercurrentWarningTest(GCode): + """M917: Find L6474 minimum current thresholds""" + param_letters = "JXYZEFITK" + dialects = ['marlin2'] + word_key = Word('M', 917) + +class GCodeM918LSpeedWarningTest(GCode): + """M918: Find L6474 speed threshold""" + param_letters = "JXYZEITKM" + dialects = ['marlin2'] + word_key = Word('M', 918) + +class GCodeM919TmcChopperTiming(GCodeOtherModal): + """M919: Set Chopper Timing values for TMC stepper drivers""" + param_letters = "OPSITXYZABCUVW" + dialects = ['marlin2'] + word_key = Word('M', 919) + +class GCodeM928StartSdLogging(GCodeIO): + """M928: Log serial input to an SD file""" + param_letters = "e" + dialects = ['marlin2'] + word_key = Word('M', 928) + +class GCodeM951MagneticParkingExtruder(GCodeOtherModal): + """M951: Set / report Magnetic Parking Extruder settings""" + param_letters = "LRIJHDC" + dialects = ['marlin2'] + word_key = Word('M', 951) + +class GCodeM993BackUpFlashSettingsToSd(GCodeIO): + """M993: Create a backup of SPI Flash to SD""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 993) + +class GCodeM994RestoreFlashFromSd(GCodeIO): + """M994: Restore a backup from SD to SPI Flash""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 994) + +class GCodeM995TouchScreenCalibration(GCodeIO): + """M995: Touch screen calibration for TFT display""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 995) + +class GCodeM997FirmwareUpdate(GCode): + """M997: Perform in-application firmware update""" + param_letters = "" + dialects = ['marlin2'] + word_key = Word('M', 997) + +class GCodeM999StopRestart(GCodeOtherModal): + """M999: Return the machine to Running state""" + param_letters = "S" + dialects = ['marlin2'] + word_key = Word('M', 999) + +class GCodeT0SelectOrReportTool(GCodeOtherModal): + """T0, T1, T2, T3, T4, T5, T6, T7: Set or report the current extruder or other tool""" + param_letters = "FS" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'T') and (w.value in [0, 1, 2, 3, 4, 5, 6, 7]) + default_word = Word('T', 0) + +class GCodeT1MmuSpecialCommands(GCode): + """'T?', Tc, Tx: MMU2 special filament loading commands""" + param_letters = "" + dialects = ['marlin2'] + + @classmethod + def word_matches(cls, w): + return (w.letter == 'T') and (w.value in ['?', 'c', 'x']) + default_word = Word('T', 1) + diff --git a/src/pygcode/gcodes_prusa.py b/src/pygcode/gcodes_prusa.py new file mode 100644 index 0000000..24c2374 --- /dev/null +++ b/src/pygcode/gcodes_prusa.py @@ -0,0 +1,117 @@ +from .gcodes_base import * + +from gcodes_legacy import * + +# ======================= Prusa ======================= +# CODE PARAMETERS DESCRIPTION +# M862.1 P Q Nozzle Diameter +# M862.2 P Q Model Code +# M862.3 P Q Model Name +# M862.4 P Q Firmware Version +# M862.5 P Q GCode Level +# M115 V U Firmware info +# M73 P R Q S C D Set/Get print progress +# M205 S T B X Y Z E Set advanced settings +# M104 S Set extruder temperature +# M109 B R S Wait for extruder temperature +# M140 S Set bed temperature +# M190 R S Wait for bed temperature +# M204 S T Acceleration settings +# M221 S T Set extrude factor override percentage +# M106 S Set fan speed +# G80 N R V L R F B Mesh-based Z probe + +class GCodePrintChecking(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('PQ') + +class GCodeNozzleDiameterPrintChecking(GCodePrintChecking): + """M862.1: Nozzle Diameter""" + word_key = Word('M', 862.1) + +class GCodeModelCodePrintChecking(GCodePrintChecking): + """M862.2: Model Code""" + word_key = Word('M', 862.2) + +class GCodeModelNamePrintChecking(GCodePrintChecking): + """M862.3: Model Name""" + word_key = Word('M', 862.3) + +class GCodeFirmwareVersionPrintChecking(GCodePrintChecking): + """M862.4: Firmware Version""" + word_key = Word('M', 862.4) + +class GCodeGcodeLevelPrintChecking(GCodePrintChecking): + """M862.5: Gcode Level""" + word_key = Word('M', 862.5) + +class GCodeFirmwareInfo(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('VU') + word_key = Word('M', 115) + +class GCodePrintProgress(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('PRQSCD') + word_key = Word('M', 73) + +class GCodeSetAdvancedSettings(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('STBXYZE') + word_key = Word('M', 205) + +class GCodeSetExtruderTemperature(GCode): + """M104: Set Extruder Temperature""" + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + word_key = Word('M', 104) + param_letters = set('S') + +class GCodeWaitForExtruderTemperature(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('BRS') + word_key = Word('M', 109) + +class GCodeSetBedTemperature(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('S') + word_key = Word('M', 140) + +class GCodeWaitForBedTemperature(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('RS') + word_key = Word('M', 190) + +class GCodeAccelerationSettings(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('ST') + word_key = Word('M', 204) + +class GCodeSetExtrudeFactorOverridePercentage(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('ST') + word_key = Word('M', 221) + +class GCodeSetFanSpeed(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('S') + word_key = Word('M', 106) + +class GCodeMeshBasedZProbe(GCode): + exec_order = 999 + modal_group = MODAL_GROUP_MAP['user_defined'] + param_letters = set('NRVLRFB') + word_key = Word('G', 80) + + +