added split_gcode

This commit is contained in:
Peter Boin 2017-07-09 18:03:22 +10:00
parent 988ee9db94
commit 2cae923587
5 changed files with 451 additions and 268 deletions

View File

@ -11,67 +11,84 @@ __all__ = [
'Word', 'text2words', 'str2word', 'words2dict', 'Word', 'text2words', 'str2word', 'words2dict',
# GCodes # GCodes
'GCode', 'words2gcodes', 'words2gcodes', 'text2gcodes', 'split_gcodes',
'GCode',
'GCodeAbsoluteArcDistanceMode',
'GCodeAbsoluteDistanceMode',
'GCodeAdaptiveFeed',
'GCodeAddToolLengthOffset',
'GCodeAnalogOutput',
'GCodeAnalogOutputImmediate',
'GCodeAnalogOutputSyncd',
'GCodeArcMove',
'GCodeArcMoveCCW',
'GCodeArcMoveCW',
'GCodeBoringCycleDwellFeedOut', 'GCodeBoringCycleDwellFeedOut',
'GCodeBoringCycleFeedOut', 'GCodeBoringCycleFeedOut',
'GCodeDrillingCycle', 'GCodeCancelCannedCycle',
'GCodeDrillingCycleChipBreaking', 'GCodeCancelToolLengthOffset',
'GCodeDrillingCycleDwell', 'GCodeCannedCycle',
'GCodeDrillingCyclePeck',
'GCodeThreadingCycle',
'GCodeCannedCycleReturnLevel', 'GCodeCannedCycleReturnLevel',
'GCodeCannedReturnMode',
'GCodeCoolant',
'GCodeCoolantFloodOn', 'GCodeCoolantFloodOn',
'GCodeCoolantMistOn', 'GCodeCoolantMistOn',
'GCodeCoolantOff', 'GCodeCoolantOff',
'GCodeCoordSystemOffset',
'GCodeCublcSpline',
'GCodeCutterCompLeft', 'GCodeCutterCompLeft',
'GCodeCutterCompRight', 'GCodeCutterCompRight',
'GCodeCutterRadiusComp',
'GCodeCutterRadiusCompOff', 'GCodeCutterRadiusCompOff',
'GCodeDynamicCutterCompLeft', 'GCodeDigitalOutput',
'GCodeDynamicCutterCompRight',
'GCodeAbsoluteArcDistanceMode',
'GCodeAbsoluteDistanceMode',
'GCodeIncrementalArcDistanceMode',
'GCodeIncrementalDistanceMode',
'GCodeLatheDiameterMode',
'GCodeLatheRadiusMode',
'GCodeInverseTimeMode',
'GCodeUnitsPerMinuteMode',
'GCodeUnitsPerRevolution',
'GCodeAnalogOutputImmediate',
'GCodeAnalogOutputSyncd',
'GCodeDigitalOutputOff', 'GCodeDigitalOutputOff',
'GCodeDigitalOutputOffSyncd', 'GCodeDigitalOutputOffSyncd',
'GCodeDigitalOutputOn', 'GCodeDigitalOutputOn',
'GCodeDigitalOutputOnSyncd', 'GCodeDigitalOutputOnSyncd',
'GCodeWaitOnInput', 'GCodeDistanceMode',
'GCodeArcMove', 'GCodeDrillingCycle',
'GCodeArcMoveCCW', 'GCodeDrillingCycleChipBreaking',
'GCodeArcMoveCW', 'GCodeDrillingCycleDwell',
'GCodeCancelCannedCycle', 'GCodeDrillingCyclePeck',
'GCodeCublcSpline',
'GCodeDwell', 'GCodeDwell',
'GCodeLinearMove', 'GCodeDynamicCutterCompLeft',
'GCodeNURBS', 'GCodeDynamicCutterCompRight',
'GCodeNURBSEnd', 'GCodeDynamicToolLengthOffset',
'GCodeQuadraticSpline', 'GCodeEndProgram',
'GCodeRapidMove', 'GCodeEndProgramPalletShuttle',
'GCodeRigidTapping', 'GCodeExactPathMode',
'GCodeSpindleSyncMotion', 'GCodeExactStopMode',
'GCodeStraightProbe',
'GCodeCoordSystemOffset',
'GCodeGotoPredefinedPosition',
'GCodeMoveInMachineCoords',
'GCodeResetCoordSystemOffset',
'GCodeRestoreCoordSystemOffset',
'GCodeSet',
'GCodeSetPredefinedPosition',
'GCodeToolChange',
'GCodeToolSetCurrent',
'GCodeUserDefined',
'GCodeAdaptiveFeed',
'GCodeFeedOverride', 'GCodeFeedOverride',
'GCodeFeedRate', 'GCodeFeedRate',
'GCodeFeedRateMode',
'GCodeFeedStop', 'GCodeFeedStop',
'GCodeGotoPredefinedPosition',
'GCodeIO',
'GCodeIncrementalArcDistanceMode',
'GCodeIncrementalDistanceMode',
'GCodeInverseTimeMode',
'GCodeLatheDiameterMode',
'GCodeLatheRadiusMode',
'GCodeLinearMove',
'GCodeMotion',
'GCodeMoveInMachineCoords',
'GCodeNURBS',
'GCodeNURBSEnd',
'GCodeNonModal',
'GCodeOrientSpindle',
'GCodeOtherModal',
'GCodePalletChangePause',
'GCodePathBlendingMode',
'GCodePathControlMode',
'GCodePauseProgram',
'GCodePauseProgramOptional',
'GCodePlaneSelect',
'GCodeProgramControl',
'GCodeQuadraticSpline',
'GCodeRapidMove',
'GCodeResetCoordSystemOffset',
'GCodeRestoreCoordSystemOffset',
'GCodeRigidTapping',
'GCodeSelectCoordinateSystem', 'GCodeSelectCoordinateSystem',
'GCodeSelectCoordinateSystem1', 'GCodeSelectCoordinateSystem1',
'GCodeSelectCoordinateSystem2', 'GCodeSelectCoordinateSystem2',
@ -83,36 +100,40 @@ __all__ = [
'GCodeSelectCoordinateSystem8', 'GCodeSelectCoordinateSystem8',
'GCodeSelectCoordinateSystem9', 'GCodeSelectCoordinateSystem9',
'GCodeSelectTool', 'GCodeSelectTool',
'GCodeSpeedAndFeedOverrideOff',
'GCodeSpeedAndFeedOverrideOn',
'GCodeSpindleSpeed',
'GCodeSpindleSpeedOverride',
'GCodeExactPathMode',
'GCodeExactStopMode',
'GCodePathBlendingMode',
'GCodeSelectUVPlane', 'GCodeSelectUVPlane',
'GCodeSelectVWPlane', 'GCodeSelectVWPlane',
'GCodeSelectWUPlane', 'GCodeSelectWUPlane',
'GCodeSelectXYPlane', 'GCodeSelectXYPlane',
'GCodeSelectYZPlane', 'GCodeSelectYZPlane',
'GCodeSelectZXPlane', 'GCodeSelectZXPlane',
'GCodeEndProgram', 'GCodeSet',
'GCodeEndProgramPalletShuttle', 'GCodeSetPredefinedPosition',
'GCodePalletChangePause', 'GCodeSpeedAndFeedOverrideOff',
'GCodePauseProgram', 'GCodeSpeedAndFeedOverrideOn',
'GCodePauseProgramOptional', 'GCodeSpindle',
'GCodeOrientSpindle',
'GCodeSpindleConstantSurfaceSpeedMode', 'GCodeSpindleConstantSurfaceSpeedMode',
'GCodeSpindleRPMMode', 'GCodeSpindleRPMMode',
'GCodeSpindleSpeed',
'GCodeSpindleSpeedMode',
'GCodeSpindleSpeedOverride',
'GCodeSpindleSyncMotion',
'GCodeStartSpindle',
'GCodeStartSpindleCCW', 'GCodeStartSpindleCCW',
'GCodeStartSpindleCW', 'GCodeStartSpindleCW',
'GCodeStopSpindle', 'GCodeStopSpindle',
'GCodeAddToolLengthOffset', 'GCodeStraightProbe',
'GCodeCancelToolLengthOffset', 'GCodeThreadingCycle',
'GCodeDynamicToolLengthOffset', 'GCodeToolChange',
'GCodeToolLength',
'GCodeToolLengthOffset', 'GCodeToolLengthOffset',
'GCodeToolSetCurrent',
'GCodeUnit',
'GCodeUnitsPerMinuteMode',
'GCodeUnitsPerRevolution',
'GCodeUseInches', 'GCodeUseInches',
'GCodeUseMillimeters', 'GCodeUseMillimeters',
'GCodeUserDefined',
'GCodeWaitOnInput',
] ]
# Machine # Machine
@ -139,7 +160,7 @@ from .words import (
# GCode # GCode
from .gcodes import ( from .gcodes import (
GCode, words2gcodes, words2gcodes, text2gcodes, split_gcodes,
# $ python -c "from pygcode.gcodes import _gcode_class_infostr; print(_gcode_class_infostr())" # $ python -c "from pygcode.gcodes import _gcode_class_infostr; print(_gcode_class_infostr())"
# - GCode: # - GCode:
@ -249,8 +270,10 @@ from .gcodes import (
# M01 - GCodePauseProgramOptional: M1: Program Pause (optional) # M01 - GCodePauseProgramOptional: M1: Program Pause (optional)
# - GCodeSpindle: # - GCodeSpindle:
# M19 - GCodeOrientSpindle: M19: Orient Spindle # M19 - GCodeOrientSpindle: M19: Orient Spindle
# - GCodeSpindleSpeedMode:
# G96 - GCodeSpindleConstantSurfaceSpeedMode: G96: Spindle Constant Surface Speed # G96 - GCodeSpindleConstantSurfaceSpeedMode: G96: Spindle Constant Surface Speed
# G97 - GCodeSpindleRPMMode: G97: Spindle RPM Speed # G97 - GCodeSpindleRPMMode: G97: Spindle RPM Speed
# - GCodeStartSpindle: M3,M4: Start Spindle Clockwise
# M04 - GCodeStartSpindleCCW: M4: Start Spindle Counter-Clockwise # M04 - GCodeStartSpindleCCW: M4: Start Spindle Counter-Clockwise
# M03 - GCodeStartSpindleCW: M3: Start Spindle Clockwise # M03 - GCodeStartSpindleCW: M3: Start Spindle Clockwise
# M05 - GCodeStopSpindle: M5: Stop Spindle # M05 - GCodeStopSpindle: M5: Stop Spindle
@ -263,66 +286,84 @@ from .gcodes import (
# G20 - GCodeUseInches: G20: use inches for length units # G20 - GCodeUseInches: G20: use inches for length units
# G21 - GCodeUseMillimeters: G21: use millimeters for length units # G21 - GCodeUseMillimeters: G21: use millimeters for length units
# $ python -c "from pygcode.gcodes import GCode, _subclasses; print(',\\n'.join(sorted(g.__name__ for g in _subclasses(GCode))))"python -c "from pygcode.gcodes import GCode, _subclasses; print(',\\n'.join(sorted(g.__name__ for g in _subclasses(GCode))))"
GCode,
GCodeAbsoluteArcDistanceMode,
GCodeAbsoluteDistanceMode,
GCodeAdaptiveFeed,
GCodeAddToolLengthOffset,
GCodeAnalogOutput,
GCodeAnalogOutputImmediate,
GCodeAnalogOutputSyncd,
GCodeArcMove,
GCodeArcMoveCCW,
GCodeArcMoveCW,
GCodeBoringCycleDwellFeedOut, GCodeBoringCycleDwellFeedOut,
GCodeBoringCycleFeedOut, GCodeBoringCycleFeedOut,
GCodeDrillingCycle, GCodeCancelCannedCycle,
GCodeDrillingCycleChipBreaking, GCodeCancelToolLengthOffset,
GCodeDrillingCycleDwell, GCodeCannedCycle,
GCodeDrillingCyclePeck,
GCodeThreadingCycle,
GCodeCannedCycleReturnLevel, GCodeCannedCycleReturnLevel,
GCodeCannedReturnMode,
GCodeCoolant,
GCodeCoolantFloodOn, GCodeCoolantFloodOn,
GCodeCoolantMistOn, GCodeCoolantMistOn,
GCodeCoolantOff, GCodeCoolantOff,
GCodeCoordSystemOffset,
GCodeCublcSpline,
GCodeCutterCompLeft, GCodeCutterCompLeft,
GCodeCutterCompRight, GCodeCutterCompRight,
GCodeCutterRadiusComp,
GCodeCutterRadiusCompOff, GCodeCutterRadiusCompOff,
GCodeDynamicCutterCompLeft, GCodeDigitalOutput,
GCodeDynamicCutterCompRight,
GCodeAbsoluteArcDistanceMode,
GCodeAbsoluteDistanceMode,
GCodeIncrementalArcDistanceMode,
GCodeIncrementalDistanceMode,
GCodeLatheDiameterMode,
GCodeLatheRadiusMode,
GCodeInverseTimeMode,
GCodeUnitsPerMinuteMode,
GCodeUnitsPerRevolution,
GCodeAnalogOutputImmediate,
GCodeAnalogOutputSyncd,
GCodeDigitalOutputOff, GCodeDigitalOutputOff,
GCodeDigitalOutputOffSyncd, GCodeDigitalOutputOffSyncd,
GCodeDigitalOutputOn, GCodeDigitalOutputOn,
GCodeDigitalOutputOnSyncd, GCodeDigitalOutputOnSyncd,
GCodeWaitOnInput, GCodeDistanceMode,
GCodeArcMove, GCodeDrillingCycle,
GCodeArcMoveCCW, GCodeDrillingCycleChipBreaking,
GCodeArcMoveCW, GCodeDrillingCycleDwell,
GCodeCancelCannedCycle, GCodeDrillingCyclePeck,
GCodeCublcSpline,
GCodeDwell, GCodeDwell,
GCodeLinearMove, GCodeDynamicCutterCompLeft,
GCodeNURBS, GCodeDynamicCutterCompRight,
GCodeNURBSEnd, GCodeDynamicToolLengthOffset,
GCodeQuadraticSpline, GCodeEndProgram,
GCodeRapidMove, GCodeEndProgramPalletShuttle,
GCodeRigidTapping, GCodeExactPathMode,
GCodeSpindleSyncMotion, GCodeExactStopMode,
GCodeStraightProbe,
GCodeCoordSystemOffset,
GCodeGotoPredefinedPosition,
GCodeMoveInMachineCoords,
GCodeResetCoordSystemOffset,
GCodeRestoreCoordSystemOffset,
GCodeSet,
GCodeSetPredefinedPosition,
GCodeToolChange,
GCodeToolSetCurrent,
GCodeUserDefined,
GCodeAdaptiveFeed,
GCodeFeedOverride, GCodeFeedOverride,
GCodeFeedRate, GCodeFeedRate,
GCodeFeedRateMode,
GCodeFeedStop, GCodeFeedStop,
GCodeGotoPredefinedPosition,
GCodeIO,
GCodeIncrementalArcDistanceMode,
GCodeIncrementalDistanceMode,
GCodeInverseTimeMode,
GCodeLatheDiameterMode,
GCodeLatheRadiusMode,
GCodeLinearMove,
GCodeMotion,
GCodeMoveInMachineCoords,
GCodeNURBS,
GCodeNURBSEnd,
GCodeNonModal,
GCodeOrientSpindle,
GCodeOtherModal,
GCodePalletChangePause,
GCodePathBlendingMode,
GCodePathControlMode,
GCodePauseProgram,
GCodePauseProgramOptional,
GCodePlaneSelect,
GCodeProgramControl,
GCodeQuadraticSpline,
GCodeRapidMove,
GCodeResetCoordSystemOffset,
GCodeRestoreCoordSystemOffset,
GCodeRigidTapping,
GCodeSelectCoordinateSystem, GCodeSelectCoordinateSystem,
GCodeSelectCoordinateSystem1, GCodeSelectCoordinateSystem1,
GCodeSelectCoordinateSystem2, GCodeSelectCoordinateSystem2,
@ -334,34 +375,38 @@ from .gcodes import (
GCodeSelectCoordinateSystem8, GCodeSelectCoordinateSystem8,
GCodeSelectCoordinateSystem9, GCodeSelectCoordinateSystem9,
GCodeSelectTool, GCodeSelectTool,
GCodeSpeedAndFeedOverrideOff,
GCodeSpeedAndFeedOverrideOn,
GCodeSpindleSpeed,
GCodeSpindleSpeedOverride,
GCodeExactPathMode,
GCodeExactStopMode,
GCodePathBlendingMode,
GCodeSelectUVPlane, GCodeSelectUVPlane,
GCodeSelectVWPlane, GCodeSelectVWPlane,
GCodeSelectWUPlane, GCodeSelectWUPlane,
GCodeSelectXYPlane, GCodeSelectXYPlane,
GCodeSelectYZPlane, GCodeSelectYZPlane,
GCodeSelectZXPlane, GCodeSelectZXPlane,
GCodeEndProgram, GCodeSet,
GCodeEndProgramPalletShuttle, GCodeSetPredefinedPosition,
GCodePalletChangePause, GCodeSpeedAndFeedOverrideOff,
GCodePauseProgram, GCodeSpeedAndFeedOverrideOn,
GCodePauseProgramOptional, GCodeSpindle,
GCodeOrientSpindle,
GCodeSpindleConstantSurfaceSpeedMode, GCodeSpindleConstantSurfaceSpeedMode,
GCodeSpindleRPMMode, GCodeSpindleRPMMode,
GCodeSpindleSpeed,
GCodeSpindleSpeedMode,
GCodeSpindleSpeedOverride,
GCodeSpindleSyncMotion,
GCodeStartSpindle,
GCodeStartSpindleCCW, GCodeStartSpindleCCW,
GCodeStartSpindleCW, GCodeStartSpindleCW,
GCodeStopSpindle, GCodeStopSpindle,
GCodeAddToolLengthOffset, GCodeStraightProbe,
GCodeCancelToolLengthOffset, GCodeThreadingCycle,
GCodeDynamicToolLengthOffset, GCodeToolChange,
GCodeToolLength,
GCodeToolLengthOffset, GCodeToolLengthOffset,
GCodeToolSetCurrent,
GCodeUnit,
GCodeUnitsPerMinuteMode,
GCodeUnitsPerRevolution,
GCodeUseInches, GCodeUseInches,
GCodeUseMillimeters, GCodeUseMillimeters,
GCodeUserDefined,
GCodeWaitOnInput,
) )

View File

@ -1,9 +1,9 @@
from collections import defaultdict from collections import defaultdict
from copy import copy from copy import copy
from .words import Word from .words import Word, text2words
from .exceptions import GCodeParameterError from .exceptions import GCodeParameterError, GCodeWordStrError
# Terminology of a "G-Code" # Terminology of a "G-Code"
# For the purposes of this library, so-called "G" codes do not necessarily # For the purposes of this library, so-called "G" codes do not necessarily
@ -138,6 +138,7 @@ class GCode(object):
# Defining Word # Defining Word
word_key = None # Word instance to use in lookup word_key = None # Word instance to use in lookup
word_matches = None # function (secondary) word_matches = None # function (secondary)
default_word = None
# Parameters associated to this gcode # Parameters associated to this gcode
param_letters = set() param_letters = set()
@ -149,18 +150,24 @@ class GCode(object):
# Execution Order # Execution Order
exec_order = 999 # if not otherwise specified, run last exec_order = 999 # if not otherwise specified, run last
def __init__(self, word, *params): def __init__(self, *words):
""" """
:param word: Word instance defining gcode (eg: Word('G0') for rapid movement) :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) :param params: list of Word instances (eg: Word('X-1.2') as x-coordinate)
""" """
assert isinstance(word, Word), "invalid gcode word %r" % code_word gcode_word_list = words[:1]
self.word = word param_words = words[1:]
if gcode_word_list:
gcode_word = gcode_word_list[0]
else:
gcode_word = self._default_word()
assert isinstance(gcode_word, Word), "invalid gcode word %r" % gcode_word
self.word = gcode_word
self.params = {} self.params = {}
# Add Given Parameters # Add Given Parameters
for param in params: for param_word in param_words:
self.add_parameter(param) self.add_parameter(param_word)
def __repr__(self): def __repr__(self):
param_str = '' param_str = ''
@ -188,10 +195,23 @@ class GCode(object):
parameters=param_str, parameters=param_str,
) )
# Sort by exec_order 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__)
# Comparisons
def __lt__(self, other): def __lt__(self, other):
"""Sort by execution order"""
return self.exec_order < other.exec_order return self.exec_order < other.exec_order
def __gt__(self, other):
"""Sort by execution order"""
return self.exec_order > other.exec_order
# Parameters
def add_parameter(self, word): def add_parameter(self, word):
""" """
Add given word as a parameter for this gcode Add given word as a parameter for this gcode
@ -355,6 +375,7 @@ class GCodeStraightProbe(GCodeMotion):
@classmethod @classmethod
def word_matches(cls, w): def word_matches(cls, w):
return (w.letter == 'G') and (38.2 <= w.value <= 38.5) return (w.letter == 'G') and (38.2 <= w.value <= 38.5)
default_word = Word('G', 38.2)
class GCodeSpindleSyncMotion(GCodeMotion): class GCodeSpindleSyncMotion(GCodeMotion):
@ -512,17 +533,20 @@ class GCodeSpindle(GCode):
exec_order = 90 exec_order = 90
class GCodeStartSpindleCW(GCodeSpindle): class GCodeStartSpindle(GCodeSpindle):
"""M3,M4: Start Spindle Clockwise"""
modal_group = MODAL_GROUP_MAP['spindle']
class GCodeStartSpindleCW(GCodeStartSpindle):
"""M3: Start Spindle Clockwise""" """M3: Start Spindle Clockwise"""
#param_letters = set('S') # S is it's own gcode, makes no sense to be here #param_letters = set('S') # S is it's own gcode, makes no sense to be here
word_key = Word('M', 3) word_key = Word('M', 3)
modal_group = MODAL_GROUP_MAP['spindle']
class GCodeStartSpindleCCW(GCodeSpindle): class GCodeStartSpindleCCW(GCodeStartSpindle):
"""M4: Start Spindle Counter-Clockwise""" """M4: Start Spindle Counter-Clockwise"""
#param_letters = set('S') # S is it's own gcode, makes no sense to be here #param_letters = set('S') # S is it's own gcode, makes no sense to be here
word_key = Word('M', 4) word_key = Word('M', 4)
modal_group = MODAL_GROUP_MAP['spindle']
class GCodeStopSpindle(GCodeSpindle): class GCodeStopSpindle(GCodeSpindle):
@ -537,18 +561,20 @@ class GCodeOrientSpindle(GCodeSpindle):
word_key = Word('M', 19) word_key = Word('M', 19)
class GCodeSpindleConstantSurfaceSpeedMode(GCodeSpindle): class GCodeSpindleSpeedMode(GCodeSpindle):
modal_group = MODAL_GROUP_MAP['spindle_speed_mode']
class GCodeSpindleConstantSurfaceSpeedMode(GCodeSpindleSpeedMode):
"""G96: Spindle Constant Surface Speed""" """G96: Spindle Constant Surface Speed"""
param_letters = set('DS') param_letters = set('DS')
word_key = Word('G', 96) word_key = Word('G', 96)
modal_group = MODAL_GROUP_MAP['spindle_speed_mode']
class GCodeSpindleRPMMode(GCodeSpindle): class GCodeSpindleRPMMode(GCodeSpindleSpeedMode):
"""G97: Spindle RPM Speed""" """G97: Spindle RPM Speed"""
param_letters = set('D') param_letters = set('D')
word_key = Word('G', 97) word_key = Word('G', 97)
modal_group = MODAL_GROUP_MAP['spindle_speed_mode']
@ -807,6 +833,7 @@ class GCodeFeedRate(GCodeOtherModal):
@classmethod @classmethod
def word_matches(cls, w): def word_matches(cls, w):
return w.letter == 'F' return w.letter == 'F'
default_word = Word('F', 0)
modal_group = MODAL_GROUP_MAP['feed_rate'] modal_group = MODAL_GROUP_MAP['feed_rate']
exec_order = 40 exec_order = 40
@ -816,6 +843,7 @@ class GCodeSpindleSpeed(GCodeOtherModal):
@classmethod @classmethod
def word_matches(cls, w): def word_matches(cls, w):
return w.letter == 'S' return w.letter == 'S'
default_word = Word('S', 0)
# Modal Group: (see description in GCodeFeedRate) # Modal Group: (see description in GCodeFeedRate)
modal_group = MODAL_GROUP_MAP['spindle_speed'] modal_group = MODAL_GROUP_MAP['spindle_speed']
exec_order = 50 exec_order = 50
@ -826,6 +854,7 @@ class GCodeSelectTool(GCodeOtherModal):
@classmethod @classmethod
def word_matches(cls, w): def word_matches(cls, w):
return w.letter == 'T' return w.letter == 'T'
default_word = Word('T', 0)
# Modal Group: (see description in GCodeFeedRate) # Modal Group: (see description in GCodeFeedRate)
modal_group = MODAL_GROUP_MAP['tool'] modal_group = MODAL_GROUP_MAP['tool']
exec_order = 60 exec_order = 60
@ -1052,6 +1081,7 @@ class GCodeGotoPredefinedPosition(GCodeNonModal):
@classmethod @classmethod
def word_matches(cls, w): def word_matches(cls, w):
return (w.letter == 'G') and (w.value in [28, 30]) return (w.letter == 'G') and (w.value in [28, 30])
default_word = Word('G', 28)
exec_order = 230 exec_order = 230
@ -1060,6 +1090,7 @@ class GCodeSetPredefinedPosition(GCodeNonModal):
@classmethod @classmethod
def word_matches(cls, w): def word_matches(cls, w):
return (w.letter == 'G') and (w.value in [28.1, 30.1]) return (w.letter == 'G') and (w.value in [28.1, 30.1])
default_word = Word('G', 28.1)
exec_order = 230 exec_order = 230
@ -1080,6 +1111,7 @@ class GCodeResetCoordSystemOffset(GCodeNonModal):
@classmethod @classmethod
def word_matches(cls, w): def word_matches(cls, w):
return (w.letter == 'G') and (w.value in [92.1, 92.2]) return (w.letter == 'G') and (w.value in [92.1, 92.2])
default_word = Word('G', 92.1)
exec_order = 230 exec_order = 230
# TODO: machine.state.offset *= 0 # TODO: machine.state.offset *= 0
@ -1098,6 +1130,7 @@ class GCodeUserDefined(GCodeNonModal):
#@classmethod #@classmethod
#def word_matches(cls, w): #def word_matches(cls, w):
# return (w.letter == 'M') and (101 <= w.value <= 199) # return (w.letter == 'M') and (101 <= w.value <= 199)
#default_word = Word('M', 101)
exec_order = 130 exec_order = 130
modal_group = MODAL_GROUP_MAP['user_defined'] modal_group = MODAL_GROUP_MAP['user_defined']
@ -1171,6 +1204,7 @@ def build_maps():
_gcode_maps_created = True _gcode_maps_created = True
# ======================= Words -> GCodes =======================
def word_gcode_class(word, exhaustive=False): def word_gcode_class(word, exhaustive=False):
""" """
Map word to corresponding GCode class Map word to corresponding GCode class
@ -1179,7 +1213,7 @@ def word_gcode_class(word, exhaustive=False):
:return: class inheriting GCode :return: class inheriting GCode
""" """
if _gcode_maps_created is False: if not _gcode_maps_created:
build_maps() build_maps()
# quickly eliminate parameters # quickly eliminate parameters
@ -1197,6 +1231,7 @@ def word_gcode_class(word, exhaustive=False):
return None return None
def words2gcodes(words): def words2gcodes(words):
""" """
Group words into g-codes (includes both G & M codes) Group words into g-codes (includes both G & M codes)
@ -1257,3 +1292,51 @@ def words2gcodes(words):
gcodes.append(gcode) gcodes.append(gcode)
return (gcodes, parameter_map[None]) return (gcodes, parameter_map[None])
def text2gcodes(text):
"""
Convert text to GCode instances (must be fully formed; no modal parameters)
:param text: line from a g-code file
:return: tuple([<GCode>, <GCode>, ...], list(<unused words>))
"""
words = list(text2words(text))
(gcodes, modal_words) = words2gcodes(words)
if modal_words:
raise GCodeWordStrError("gcode text not fully formed, unassigned parameters: %r" % modal_words)
return gcodes
# ======================= Utilities =======================
def split_gcodes(gcode_list, splitter_class, sort_list=True):
"""
Splits a list of GCode instances into 3, the center list containing the splitter_class gcode
:param gcode_list: list of GCode instances to split
:param splitter_class: class of gcode identifying split from left to right
:return: list of: [[<gcodes before splitter>], [<splitter instance>], [<gcodes after splitter>]]
"""
# for example:
# g_list = sorted([g1, g2, g3, g4])
# split_gcodes(g_list, type(g2)) == [[g1], [g2], [g3, g4]]
# 3 lists are always returned, even if empty; if 2nd list is empty,
# then the 3rd will be as well.
if sort_list: # sort by execution order first
gcode_list = sorted(gcode_list)
split = [gcode_list, [], []] # default (if no splitter can be found)
# Find splitter index (only one can be found)
split_index = None
for (i, gcode) in enumerate(gcode_list):
if isinstance(gcode, splitter_class):
split_index = i
break
# Form split: pivoting around split_index
if split_index is not None:
split[0] = gcode_list[:split_index]
split[1] = [gcode_list[split_index]]
split[2] = gcode_list[split_index+1:]
return split

View File

@ -12,7 +12,6 @@ class Line(object):
# Split line into block text, and comments # Split line into block text, and comments
if text is not None: if text is not None:
(block_str, comment) = split_line(text) (block_str, comment) = split_line(text)
if block_str:
self.block = Block(block_str) self.block = Block(block_str)
if comment: if comment:
self.comment = comment self.comment = comment
@ -23,5 +22,10 @@ class Line(object):
return str(self) return str(self)
return self._text return self._text
@property
def gcodes(self):
"""self.block.gcodes passthrough"""
return self.block.gcodes
def __str__(self): def __str__(self):
return ' '.join([str(x) for x in [self.block, self.comment] if x]) return ' '.join([str(x) for x in [self.block, self.comment] if x])

View File

@ -253,7 +253,7 @@ class Mode(object):
def set_mode(self, *gcode_list): def set_mode(self, *gcode_list):
""" """
Set machine mode from given gcodes Set machine mode from given gcodes (will not be processed)
:param gcode_list: list of GCode instances (given as individual parameters) :param gcode_list: list of GCode instances (given as individual parameters)
:return: dict of form: {<modal group>: <new mode GCode>, ...} :return: dict of form: {<modal group>: <new mode GCode>, ...}
""" """
@ -291,12 +291,17 @@ class Mode(object):
else: else:
self.__dict__[key] = value self.__dict__[key] = value
def __str__(self): @property
def gcodes(self):
"""List of modal gcodes"""
gcode_list = [] gcode_list = []
for modal_group in sorted(MODAL_GROUP_MAP.values()): for modal_group in sorted(MODAL_GROUP_MAP.values()):
if self.modal_groups[modal_group]: if self.modal_groups[modal_group]:
gcode_list.append(self.modal_groups[modal_group]) gcode_list.append(self.modal_groups[modal_group])
return ' '.join(str(g) for g in gcode_list) return gcode_list
def __str__(self):
return ' '.join(str(g) for g in self.gcodes)
def __repr__(self): def __repr__(self):
return "<{class_name}: {gcodes}>".format( return "<{class_name}: {gcodes}>".format(
@ -318,9 +323,10 @@ class Machine(object):
self.state = self.STATE_CLASS(axes=self.axes) self.state = self.STATE_CLASS(axes=self.axes)
# Position type (with default axes the same as this machine) # Position type (with default axes the same as this machine)
units_mode = getattr(self.mode, 'units', None)
self.Position = type('Position', (Position,), { self.Position = type('Position', (Position,), {
'default_axes': self.axes, 'default_axes': self.axes,
'default_unit': self.mode.units.unit_id, 'default_unit': units_mode.unit_id if units_mode else UNIT_METRIC,
}) })
# Absolute machine position # Absolute machine position
@ -335,6 +341,7 @@ class Machine(object):
self.state.cur_coord_sys = coord_sys_mode.coord_system_id self.state.cur_coord_sys = coord_sys_mode.coord_system_id
def modal_gcode(self, modal_params): def modal_gcode(self, modal_params):
if not modal_params: if not modal_params:
return None return None
if self.mode.motion is None: if self.mode.motion is None:
@ -349,7 +356,7 @@ class Machine(object):
return modal_gcodes[0] return modal_gcodes[0]
return None return None
def process(self, *gcode_list, **kwargs): def process_gcodes(self, *gcode_list, **kwargs):
""" """
Process gcodes Process gcodes
:param gcode_list: list of GCode instances :param gcode_list: list of GCode instances
@ -377,7 +384,11 @@ class Machine(object):
# - Crop a file (eg: resume half way through) # - Crop a file (eg: resume half way through)
def process_block(self, block): def process_block(self, block):
self.process(*block.gcodes, modal_params=block.modal_params) self.process_gcodes(*block.gcodes, modal_params=block.modal_params)
def process_str(self, block_str):
line = Line(block_str)
self.process_block(line.block)
@property @property
def pos(self): def pos(self):

View File

@ -11,7 +11,10 @@ add_pygcode_to_path()
# Units under test # Units under test
from pygcode import gcodes from pygcode import gcodes
from pygcode import words from pygcode import words
class TestGCodeWordMapping(unittest.TestCase):
from pygcode.exceptions import GCodeWordStrError
class GCodeWordMappingTests(unittest.TestCase):
def test_word_map_integrity(self): def test_word_map_integrity(self):
gcodes.build_maps() gcodes.build_maps()
@ -24,7 +27,7 @@ class TestGCodeWordMapping(unittest.TestCase):
"conflict with %s and %s" % (fn_class, key_class) "conflict with %s and %s" % (fn_class, key_class)
) )
class TestGCodeModalGroups(unittest.TestCase): class GCodeModalGroupTests(unittest.TestCase):
def test_modal_groups(self): def test_modal_groups(self):
# Modal groups taken (and slightly modified) from LinuxCNC documentation: # Modal groups taken (and slightly modified) from LinuxCNC documentation:
# link: http://linuxcnc.org/docs/html/gcode/overview.html#_modal_groups # link: http://linuxcnc.org/docs/html/gcode/overview.html#_modal_groups
@ -80,7 +83,7 @@ class TestGCodeModalGroups(unittest.TestCase):
) )
class TestWordsToGCodes(unittest.TestCase): class Words2GCodesTests(unittest.TestCase):
def test_stuff(self): # FIXME: function name def test_stuff(self): # FIXME: function name
line = 'G1 X82.6892 Y-38.6339 F1500' line = 'G1 X82.6892 Y-38.6339 F1500'
word_list = list(words.text2words(line)) word_list = list(words.text2words(line))
@ -99,3 +102,40 @@ class TestWordsToGCodes(unittest.TestCase):
self.assertEqual(gcode_list[0].Y, -38.6339) self.assertEqual(gcode_list[0].Y, -38.6339)
# F1500 # F1500
self.assertEqual(gcode_list[1].word, words.Word('F', 1500)) self.assertEqual(gcode_list[1].word, words.Word('F', 1500))
class Text2GCodesTests(unittest.TestCase):
def test_basic(self):
gcs = gcodes.text2gcodes('G1 X1 Y2 G90')
self.assertEqual(len(gcs), 2)
# G1 X1 Y2
self.assertEqual(gcs[0].word, words.Word('G', 1))
self.assertEqual(gcs[0].X, 1)
self.assertEqual(gcs[0].Y, 2)
# G90
self.assertEqual(gcs[1].word, words.Word('G', 90))
def test_modal_params(self):
with self.assertRaises(GCodeWordStrError):
gcodes.text2gcodes('X1 Y2')
class GCodeSplitTests(unittest.TestCase):
def test_split(self):
g_list = gcodes.text2gcodes('G91 S1000 G1 X1 Y2 M3')
split = gcodes.split_gcodes(g_list, gcodes.GCodeStartSpindle)
self.assertEqual([len(x) for x in split], [1, 1, 2])
self.assertTrue(any(isinstance(g, gcodes.GCodeSpindleSpeed) for g in split[0]))
self.assertTrue(isinstance(split[1][0], gcodes.GCodeStartSpindle))
self.assertTrue(any(isinstance(g, gcodes.GCodeDistanceMode) for g in split[2]))
self.assertTrue(any(isinstance(g, gcodes.GCodeMotion) for g in split[2]))
def test_split_unsorted(self):
g_list = gcodes.text2gcodes('G91 G1 X1 Y2 M3 S1000')
split = gcodes.split_gcodes(g_list, gcodes.GCodeStartSpindle, sort_list=False)
self.assertEqual([len(x) for x in split], [2, 1, 1])
self.assertTrue(any(isinstance(g, gcodes.GCodeDistanceMode) for g in split[0]))
self.assertTrue(any(isinstance(g, gcodes.GCodeMotion) for g in split[0]))
self.assertTrue(isinstance(split[1][0], gcodes.GCodeStartSpindle))
self.assertTrue(any(isinstance(g, gcodes.GCodeSpindleSpeed) for g in split[2]))