From bba470411590faa84583a30745b5d944d323f2bc Mon Sep 17 00:00:00 2001 From: duncan law Date: Sat, 8 Feb 2020 22:52:52 +0000 Subject: [PATCH 1/5] Add parameters to G92. --- src/pygcode/gcodes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pygcode/gcodes.py b/src/pygcode/gcodes.py index a2e5f34..780ee3b 100644 --- a/src/pygcode/gcodes.py +++ b/src/pygcode/gcodes.py @@ -1265,7 +1265,7 @@ class GCodeAnalogOutputImmediate(GCodeAnalogOutput): # G28, G28.1 Go/Set Predefined Position # G30, G30.1 Go/Set Predefined Position # G53 Move in Machine Coordinates -# G92 Coordinate System Offset +# G92 ABCXYZUVW Coordinate System Offset # G92.1, G92.2 Reset G92 Offsets # G92.3 Restore G92 Offsets # M101 - M199 P Q User Defined Commands @@ -1323,6 +1323,7 @@ class GCodeMoveInMachineCoords(GCodeNonModal): class GCodeCoordSystemOffset(GCodeNonModal): """G92: Coordinate System Offset""" + param_letters = set('XYZABCUVW') word_key = Word('G', 92) exec_order = 230 From dd6114b709dedf2507c1b42c3c32228d6e0cc033 Mon Sep 17 00:00:00 2001 From: John Ladasky <33928921+bwllc@users.noreply.github.com> Date: Mon, 2 Mar 2020 14:53:03 -0800 Subject: [PATCH 2/5] Update gcodes.py Attempting to add a hash representation of GCode subclasses, so that they can be used as items in sets and dictionary keys. --- src/pygcode/gcodes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pygcode/gcodes.py b/src/pygcode/gcodes.py index a2e5f34..a5f6d3e 100644 --- a/src/pygcode/gcodes.py +++ b/src/pygcode/gcodes.py @@ -210,6 +210,14 @@ class GCode(object): 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: From 15ade2785a0994cc77b3b67890b0e59c337fcf39 Mon Sep 17 00:00:00 2001 From: David Allison Date: Thu, 14 Apr 2022 15:34:16 -0700 Subject: [PATCH 3/5] Update gcodes.py Tiny typo fix in the docs --- src/pygcode/gcodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygcode/gcodes.py b/src/pygcode/gcodes.py index a2e5f34..ea6f5d3 100644 --- a/src/pygcode/gcodes.py +++ b/src/pygcode/gcodes.py @@ -32,7 +32,7 @@ from .exceptions import GCodeParameterError, GCodeWordStrError # # Modal Groups: # Only one mode of each modal group can be active. That is to say, a -# modal g-code can only change the sate of a previously set mode if +# modal g-code can only change the state of a previously set mode if # they're in the same group. # For example: # G20 (mm), and G21 (inches) are in group 6 From 77784025a574baf39b22657ed0d9678163405b0f Mon Sep 17 00:00:00 2001 From: Emory Barlow Date: Tue, 3 May 2022 23:25:36 -0400 Subject: [PATCH 4/5] add support for some of prusas custom gcodes --- README.rst | 6 +- src/pygcode/dialects/__init__.py | 4 +- src/pygcode/dialects/prusa.py | 202 +++++++++++++++++++++++++++++++ src/pygcode/gcodes.py | 104 ++++++++++++++++ 4 files changed, 310 insertions(+), 6 deletions(-) create mode 100644 src/pygcode/dialects/prusa.py diff --git a/README.rst b/README.rst index dd94b32..ab19fa3 100644 --- a/README.rst +++ b/README.rst @@ -11,11 +11,7 @@ for python. Installation ============ -Install using ``pip`` - -``pip install pygcode`` - -or `download directly from PyPi `__ +python3 setup.py install Documentation diff --git a/src/pygcode/dialects/__init__.py b/src/pygcode/dialects/__init__.py index 9a079a3..43edacb 100644 --- a/src/pygcode/dialects/__init__.py +++ b/src/pygcode/dialects/__init__.py @@ -8,6 +8,7 @@ __all__ = [ # dialects 'linuxcnc', 'reprap', + 'prusa', ] @@ -18,9 +19,10 @@ from .mapping import word_dialect # Dialects from . import linuxcnc from . import reprap +from . import prusa -_DEFAULT = 'linuxcnc' +_DEFAULT = 'prusa' def get_default(): diff --git a/src/pygcode/dialects/prusa.py b/src/pygcode/dialects/prusa.py new file mode 100644 index 0000000..1d412dd --- /dev/null +++ b/src/pygcode/dialects/prusa.py @@ -0,0 +1,202 @@ +import re + +from .utils import WordType + +# ======================== 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, + ), + # Feed Rates + 'E': WordType( + cls=float, + value_regex=REGEX_FLOAT, + description="Precision feedrate for threading on lathes", + clean_value=CLEAN_FLOAT, + ), + '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 ======================== diff --git a/src/pygcode/gcodes.py b/src/pygcode/gcodes.py index a2e5f34..3091123 100644 --- a/src/pygcode/gcodes.py +++ b/src/pygcode/gcodes.py @@ -1299,6 +1299,7 @@ class GCodeSet(GCodeNonModal): 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]) @@ -1308,6 +1309,7 @@ class GCodeGotoPredefinedPosition(GCodeNonModal): 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]) @@ -1356,6 +1358,108 @@ class GCodeUserDefined(GCodeNonModal): 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 + +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) # ======================= Utilities ======================= From 50b55dce7706a62c3f4f3caff05e8c1d65d7ff5a Mon Sep 17 00:00:00 2001 From: Emory Barlow Date: Tue, 3 May 2022 23:49:02 -0400 Subject: [PATCH 5/5] override G80 for prusa specific --- src/pygcode/__init__.py | 4 ++-- src/pygcode/gcodes.py | 41 +++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/pygcode/__init__.py b/src/pygcode/__init__.py index 27611cb..08b0fb7 100644 --- a/src/pygcode/__init__.py +++ b/src/pygcode/__init__.py @@ -54,7 +54,7 @@ __all__ = [ 'GCodeArcMoveCW', 'GCodeBoringCycleDwellFeedOut', 'GCodeBoringCycleFeedOut', - 'GCodeCancelCannedCycle', + #'GCodeCancelCannedCycle', 'GCodeCancelToolLengthOffset', 'GCodeCannedCycle', 'GCodeCannedCycleReturnPrevLevel', @@ -336,7 +336,7 @@ from .gcodes import ( GCodeArcMoveCW, GCodeBoringCycleDwellFeedOut, GCodeBoringCycleFeedOut, - GCodeCancelCannedCycle, + #GCodeCancelCannedCycle, GCodeCancelToolLengthOffset, GCodeCannedCycle, GCodeCannedCycleReturnPrevLevel, diff --git a/src/pygcode/gcodes.py b/src/pygcode/gcodes.py index 3091123..ede4b4e 100644 --- a/src/pygcode/gcodes.py +++ b/src/pygcode/gcodes.py @@ -508,22 +508,24 @@ class GCodeRigidTapping(GCodeMotion): param_letters = GCodeMotion.param_letters | set('K') word_key = Word('G', 33.1) - -class GCodeCancelCannedCycle(GCodeMotion): - """G80: Cancel Canned Cycle""" - 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. - modal_group = None - exec_order = 241 - - def _process(self, machine): - machine.mode.motion = None +# +# Conflicts with prusa command +# +#class GCodeCancelCannedCycle(GCodeMotion): +# """G80: Cancel Canned Cycle""" +# 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. +# modal_group = None +# exec_order = 241 +# +# def _process(self, machine): +# machine.mode.motion = None # ======================= Canned Cycles ======================= @@ -1375,6 +1377,7 @@ class GCodeUserDefined(GCodeNonModal): # 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 @@ -1461,6 +1464,12 @@ class GCodeSetFanSpeed(GCode): 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) + # ======================= Utilities ======================= def _subclasses_level(root_class, recursion_level=0):