marlin dialect support ; provision for more dialects

This commit is contained in:
Jesus Zen Droïd 2024-03-08 19:29:09 +01:00
parent f4a5fd26b3
commit e650d2efb4
7 changed files with 3089 additions and 1339 deletions

View File

@ -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

View File

@ -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!!

File diff suppressed because it is too large Load Diff

423
src/pygcode/gcodes_base.py Normal file
View File

@ -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<name> 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(<your coords in X/Y plane>)
# 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

View File

@ -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']

1501
src/pygcode/gcodes_marlin.py Normal file

File diff suppressed because it is too large Load Diff

117
src/pygcode/gcodes_prusa.py Normal file
View File

@ -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)