""" Gcodec is a collection of utilities to decode and encode gcode. To run gcodec, install python 2.x on your machine, which is avaliable from http://www.python.org/download/ Then in the folder which gcodec is in, type 'python' in a shell to run the python interpreter. Finally type 'from gcodec import *' to import this program. Below is an example of gcodec use. This example is run in a terminal in the folder which contains gcodec and Screw Holder Bottom_export.gcode. >>> from gcodec import * >>> getFileText('Screw Holder Bottom_export.gcode') 'G90\nG21\nM103\nM105\nM106\nM110 S60.0\nM111 S30.0\nM108 S210.0\nM104 S235.0\nG1 X0.37 Y-4.07 Z1.9 F60.0\nM101\n .. many lines of text .. """ from __future__ import absolute_import #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. import __init__ from skeinforge_tools.skeinforge_utilities.vector3 import Vector3 from skeinforge_tools.skeinforge_utilities import euclidean import cStringIO import os import sys __author__ = "Enrique Perez (perez_enrique@yahoo.com)" __date__ = "$Date: 2008/21/04 $" __license__ = "GPL 3.0" #getFileTextInFileDirectory might not be needed anymore def addLineAndNewlineIfNecessary( line, output ): "Add the line and if the line does not end with a newline add a newline." output.write( line ) if len( line ) < 1: return if not getHasSuffix( line, '\n' ): output.write( '\n' ) def createInitFile(): "Create the __init__.py file." fileText = '__all__ = ' + str( getPythonFileNamesExceptInit() ) writeFileText( '__init__.py', fileText ) def findWords( fileNames, search ): "Find in files the search." print( search + ' is being searched for.' ) for fileName in fileNames: fileText = getFileText( fileName ) if fileText != '': whereInText = fileText.find( search ) if whereInText != - 1: print( fileName ) print( whereInText ) whereInTextFromEnd = fileText.rfind( search ) if whereInTextFromEnd != whereInText: print( whereInTextFromEnd ) def getAbsoluteFolderPath( filePath, folderName = '' ): "Get the double value of the word after the first letter." absoluteFolderPath = os.path.dirname( os.path.abspath( filePath ) ) if folderName == '': return absoluteFolderPath return os.path.join( absoluteFolderPath, folderName ) def getDoubleAfterFirstLetter( word ): "Get the double value of the word after the first letter." return float( word[ 1 : ] ) def getDoubleForLetter( letter, splitLine ): "Get the double value of the word after the first occurence of the letter in the split line." return getDoubleAfterFirstLetter( splitLine[ indexOfStartingWithSecond( letter, splitLine ) ] ) def getDoubleFromCharacterSplitLine( character, splitLine ): "Get the double value of the string after the first occurence of the character in the split line." indexOfCharacter = indexOfStartingWithSecond( character, splitLine ) if indexOfCharacter < 0: return None floatString = splitLine[ indexOfCharacter ][ 1 : ] try: return float( floatString ) except ValueError: return None def getDoubleFromCharacterSplitLineValue( character, splitLine, value ): "Get the double value of the string after the first occurence of the character in the split line, if it does not exist return the value." splitLineFloat = getDoubleFromCharacterSplitLine( character, splitLine ) if splitLineFloat == None: return value return splitLineFloat def getFeedRateMinute( feedRateMinute, splitLine ): "Get the feed rate per minute if the split line has a feed rate." indexOfF = indexOfStartingWithSecond( "F", splitLine ) if indexOfF > 0: return getDoubleAfterFirstLetter( splitLine[ indexOfF ] ) return feedRateMinute def getFilePathWithUnderscoredBasename( fileName, suffix ): "Get the file path with all spaces in the basename replaced with underscores." suffixFileName = getUntilDot( fileName ) + suffix suffixDirectoryName = os.path.dirname( suffixFileName ) suffixReplacedBaseName = os.path.basename( suffixFileName ).replace( ' ', '_' ) return os.path.join( suffixDirectoryName, suffixReplacedBaseName ) def getFilesWithFileTypesWithoutWords( fileTypes, words = [], fileInDirectory = '' ): "Get files which have a given file type, but with do not contain a word in a list." filesWithFileTypes = [] for fileType in fileTypes: filesWithFileTypes += getFilesWithFileTypeWithoutWords( fileType, words, fileInDirectory ) filesWithFileTypes.sort() return filesWithFileTypes def getFilesWithFileTypeWithoutWords( fileType, words = [], fileInDirectory = '' ): "Get files which have a given file type, but with do not contain a word in a list." filesWithFileType = [] directoryName = os.getcwd() if fileInDirectory != '': directoryName = os.path.dirname( fileInDirectory ) directory = os.listdir( directoryName ) for fileName in directory: joinedFileName = fileName if fileInDirectory != '': joinedFileName = os.path.join( directoryName, fileName ) if isFileWithFileTypeWithoutWords( fileType, joinedFileName, words ): filesWithFileType.append( joinedFileName ) filesWithFileType.sort() return filesWithFileType def getFileText( fileName, readMode = 'r', printWarning = True ): "Get the entire text of a file." try: file = open( fileName, readMode ) fileText = file.read() file.close() return fileText except IOError: if printWarning: print( 'The file ' + fileName + ' does not exist.' ) return '' def getFileTextInFileDirectory( fileInDirectory, fileName, readMode = 'r' ): "Get the entire text of a file in the directory of the file in directory." absoluteFilePathInFileDirectory = os.path.join( os.path.dirname( fileInDirectory ), fileName ) return getFileText( absoluteFilePathInFileDirectory, readMode ) def getFirstWord( splitLine ): "Get the first word of a split line." if len( splitLine ) > 0: return splitLine[ 0 ] return '' def getFirstWordFromLine( line ): "Get the first word of a line." return getFirstWord( line.split() ) def getGcodeFileText( fileName, gcodeText ): "Get the gcode text from a file if it the gcode text is empty and if the file is a gcode file." if gcodeText != '': return gcodeText if getHasSuffix( fileName, '.gcode' ): return getFileText( fileName ) return '' def getHasPrefix( name, prefix ): "Determine if the word begins with the prefix." return name[ : len( prefix ) ].lower() == prefix def getHasSuffix( name, suffix ): "Determine if the word ends with the suffix." return name[ - len( suffix ) : ].lower() == suffix def getLocationFromSplitLine( oldLocation, splitLine ): "Get the location from the split line." if oldLocation == None: oldLocation = Vector3() return Vector3( getDoubleFromCharacterSplitLineValue( 'X', splitLine, oldLocation.x ), getDoubleFromCharacterSplitLineValue( 'Y', splitLine, oldLocation.y ), getDoubleFromCharacterSplitLineValue( 'Z', splitLine, oldLocation.z ) ) def getModuleWithDirectoryPath( directoryPath, fileName ): "Get the module from the fileName and folder name." if fileName == '': print( 'The file name in getModule in gcodec was empty.' ) return None originalSystemPath = sys.path[ : ] try: sys.path.insert( 0, directoryPath ) folderPluginsModule = __import__( fileName ) sys.path = originalSystemPath return folderPluginsModule except Exception, why: sys.path = originalSystemPath print( why ) print( '' ) print( 'That error means; could not import a module with the fileName ' + fileName ) print( 'and an absolute directory name of ' + directoryPath ) print( '' ) print( 'The plugin could not be imported. So to run ' + fileName + ' directly and at least get a more informative error message,' ) print( 'in a shell in the ' + directoryPath + ' folder type ' ) print( '> python ' + fileName + '.py' ) return None def getModuleWithPath( path ): "Get the module from the path." return getModuleWithDirectoryPath( os.path.dirname( path ), os.path.basename( path ) ) def getPluginFileNamesFromDirectoryPath( directoryPath ): "Get the file names of the python plugins in the directory path." fileInDirectory = os.path.join( directoryPath, '__init__.py' ) fullPluginFileNames = getPythonFileNamesExceptInit( fileInDirectory ) pluginFileNames = [] for fullPluginFileName in fullPluginFileNames: pluginBasename = os.path.basename( fullPluginFileName ) pluginBasename = getUntilDot( pluginBasename ) pluginFileNames.append( pluginBasename ) return pluginFileNames def getPythonDirectoryNames( directoryName ): "Get the python directories." pythonDirectoryNames = [] directory = os.listdir( directoryName ) for fileName in directory: subdirectoryName = os.path.join( directoryName, fileName ) if os.path.isdir( subdirectoryName ): if os.path.isfile( os.path.join( subdirectoryName, '__init__.py' ) ): pythonDirectoryNames.append( subdirectoryName ) return pythonDirectoryNames def getPythonDirectoryNamesRecursively( directoryName = '' ): "Get the python directories recursively." recursivePythonDirectoryNames = [] if directoryName == '': directoryName = os.getcwd() if os.path.isfile( os.path.join( directoryName, '__init__.py' ) ): recursivePythonDirectoryNames.append( directoryName ) pythonDirectoryNames = getPythonDirectoryNames( directoryName ) for pythonDirectoryName in pythonDirectoryNames: recursivePythonDirectoryNames += getPythonDirectoryNamesRecursively( pythonDirectoryName ) else: return [] return recursivePythonDirectoryNames def getPythonFileNamesExceptInit( fileInDirectory = '' ): "Get the python fileNames of the directory which the fileInDirectory is in, except for the __init__.py file." pythonFileNamesExceptInit = getFilesWithFileTypeWithoutWords( 'py', [ '__init__.py' ], fileInDirectory ) pythonFileNamesExceptInit.sort() return pythonFileNamesExceptInit def getPythonFileNamesExceptInitRecursively( directoryName = '' ): "Get the python fileNames of the directory recursively, except for the __init__.py files." pythonDirectoryNames = getPythonDirectoryNamesRecursively( directoryName ) pythonFileNamesExceptInitRecursively = [] for pythonDirectoryName in pythonDirectoryNames: pythonFileNamesExceptInitRecursively += getPythonFileNamesExceptInit( os.path.join( pythonDirectoryName, '__init__.py' ) ) pythonFileNamesExceptInitRecursively.sort() return pythonFileNamesExceptInitRecursively def getSplitLineBeforeBracketSemicolon( line ): "Get the split line before a bracket or semicolon." bracketSemicolonIndex = min( line.find( ';' ), line.find( '(' ) ) if bracketSemicolonIndex < 0: return line.split() return line[ : bracketSemicolonIndex ].split() def getStringFromCharacterSplitLine( character, splitLine ): "Get the string after the first occurence of the character in the split line." indexOfCharacter = indexOfStartingWithSecond( character, splitLine ) if indexOfCharacter < 0: return None return splitLine[ indexOfCharacter ][ 1 : ] def getSummarizedFileName( fileName ): "Get the fileName basename if the file is in the current working directory, otherwise return the original full name." if os.getcwd() == os.path.dirname( fileName ): return os.path.basename( fileName ) return fileName def getTextIfEmpty( fileName, text ): "Get the text from a file if it the text is empty." if text != '': return text return getFileText( fileName ) def getTextLines( text ): "Get the all the lines of text of a text." return text.replace( '\r', '\n' ).replace( '\n\n', '\n' ).split( '\n' ) def getUnmodifiedGCodeFiles( fileInDirectory = '' ): "Get gcode files which are not modified." #transform may be needed in future but probably won't words = ' carve clip comb comment cool fill fillet hop inset oozebane raft stretch tower wipe'.replace( ' ', ' _' ).split() return getFilesWithFileTypeWithoutWords( 'gcode', words, fileInDirectory ) def getUntilDot( text ): "Get the text until the last dot, if any." dotIndex = text.rfind( '.' ) if dotIndex < 0: return text return text[ : dotIndex ] def getVersionFileName(): "Get the file name of the version date." return os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), 'version.txt' ) def getWithoutBracketsEqualTab( line ): "Get a string without the greater than sign, the bracket and less than sign, the equal sign or the tab." line = line.replace( '=', ' ' ) line = line.replace( '(<', '' ) line = line.replace( '>', '' ) return line.replace( '\t', '' ) def indexOfStartingWithSecond( letter, splitLine ): "Get index of the first occurence of the given letter in the split line, starting with the second word. Return - 1 if letter is not found" for wordIndex in xrange( 1, len( splitLine ) ): word = splitLine[ wordIndex ] firstLetter = word[ 0 ] if firstLetter == letter: return wordIndex return - 1 def isFileWithFileTypeWithoutWords( fileType, fileName, words ): "Determine if file has a given file type, but with does not contain a word in a list." fileName = os.path.basename( fileName ) fileTypeDot = '.' + fileType if not getHasSuffix( fileName, fileTypeDot ): return False for word in words: if fileName.find( word ) >= 0: return False return True def isProcedureDone( gcodeText, procedure ): "Determine if the procedure has been done on the gcode text." if gcodeText == '': return False lines = getTextLines( gcodeText ) for line in lines: withoutBracketsEqualTabQuotes = getWithoutBracketsEqualTab( line ).replace( '"', '' ) splitLine = getWithoutBracketsEqualTab( withoutBracketsEqualTabQuotes ).split() firstWord = getFirstWord( splitLine ) if firstWord == 'procedureDone': if splitLine[ 1 ].find( procedure ) != - 1: return True elif firstWord == 'extrusionStart': return False return False def isProcedureDoneOrFileIsEmpty( gcodeText, procedure ): "Determine if the procedure has been done on the gcode text or the file is empty." if gcodeText == '': return True return isProcedureDone( gcodeText, procedure ) def isThereAFirstWord( firstWord, lines, startIndex ): "Parse gcode until the first word if there is one." for lineIndex in xrange( startIndex, len( lines ) ): line = lines[ lineIndex ] splitLine = getSplitLineBeforeBracketSemicolon( line ) if firstWord == getFirstWord( splitLine ): return True return False def replaceWords( fileNames, original, replacement ): "Replace in files the original with the replacement." print( original + ' is being replaced with ' + replacement + ' in the following files:' ) for fileName in fileNames: fileText = getFileText( fileName ) if fileText != '': whereInText = fileText.find( original ) if whereInText != - 1: print( fileName ) print( whereInText ) fileText = fileText.replace( original, replacement ) writeFileText( fileName, fileText ) def writeFileMessageEnd( end, fileName, fileText, message ): "Write to a fileName with a suffix and print a message." suffixFileName = getUntilDot( fileName ) + end writeFileText( suffixFileName, fileText ) print( message + getSummarizedFileName( suffixFileName ) ) def writeFileText( fileName, fileText, writeMode = 'w+' ): "Write a text to a file." try: file = open( fileName, writeMode ) file.write( fileText ) file.close() except IOError: print( 'The file ' + fileName + ' can not be written to.' ) class BoundingRectangle: "A class to get the corners of a gcode text." def getFromGcodeLines( self, lines, radius ): "Parse gcode text and get the minimum and maximum corners." self.cornerMaximum = complex( - 999999999.0, - 999999999.0 ) self.cornerMinimum = complex( 999999999.0, 999999999.0 ) self.oldLocation = None self.cornerRadius = complex( radius, radius ) for line in lines: self.parseCorner( line ) return self def isPointInside( self, point ): "Determine if the point is inside the bounding rectangle." return point.imag >= self.cornerMinimum.imag and point.imag <= self.cornerMaximum.imag and point.real >= self.cornerMinimum.real and point.real <= self.cornerMaximum.real def parseCorner( self, line ): "Parse a gcode line and use the location to update the bounding corners." splitLine = getSplitLineBeforeBracketSemicolon( line ) firstWord = getFirstWord( splitLine ) if firstWord == '(': locationComplex = getLocationFromSplitLine( None, splitLine ).dropAxis( 2 ) self.cornerMaximum = euclidean.getMaximum( self.cornerMaximum, locationComplex ) self.cornerMinimum = euclidean.getMinimum( self.cornerMinimum, locationComplex ) elif firstWord == 'G1': location = getLocationFromSplitLine( self.oldLocation, splitLine ) locationComplex = location.dropAxis( 2 ) self.cornerMaximum = euclidean.getMaximum( self.cornerMaximum, locationComplex + self.cornerRadius ) self.cornerMinimum = euclidean.getMinimum( self.cornerMinimum, locationComplex - self.cornerRadius ) self.oldLocation = location class DistanceFeedRate: "A class to limit the z feed rate and round values." def __init__( self ): self.absoluteDistanceMode = True self.decimalPlacesCarried = 3 self.extrusionDistanceFormat = '' self.maximumZDrillFeedRatePerSecond = None self.maximumZFeedRatePerSecond = None self.maximumZTravelFeedRatePerSecond = None self.oldAddedLocation = None self.output = cStringIO.StringIO() def addGcodeFromFeedRateThreadZ( self, feedRateMinute, thread, z ): "Add a thread to the output." if len( thread ) > 0: self.addGcodeMovementZWithFeedRate( feedRateMinute, thread[ 0 ], z ) else: print( "zero length vertex positions array which was skipped over, this should never happen" ) if len( thread ) < 2: print( "thread of only one point in addGcodeFromFeedRateThreadZ in gcodec, this should never happen" ) print( thread ) return self.addLine( "M101" ) # Turn extruder on. for point in thread[ 1 : ]: self.addGcodeMovementZWithFeedRate( feedRateMinute, point, z ) self.addLine( "M103" ) # Turn extruder off. def addGcodeFromLoop( self, loop, z ): "Add the gcode loop." euclidean.addSurroundingLoopBeginning( self, loop, z ) self.addPerimeterBlock( loop, z ) self.addLine( '()' ) self.addLine( '()' ) def addGcodeFromThreadZ( self, thread, z ): "Add a thread to the output." if len( thread ) > 0: self.addGcodeMovementZ( thread[ 0 ], z ) else: print( "zero length vertex positions array which was skipped over, this should never happen" ) if len( thread ) < 2: print( "thread of only one point in addGcodeFromThreadZ in gcodec, this should never happen" ) print( thread ) return self.addLine( "M101" ) # Turn extruder on. for point in thread[ 1 : ]: self.addGcodeMovementZ( point, z ) self.addLine( "M103" ) # Turn extruder off. def addGcodeMovementZ( self, point, z ): "Add a movement to the output." self.addLine( self.getLinearGcodeMovement( point, z ) ) def addGcodeMovementZWithFeedRate( self, feedRateMinute, point, z ): "Add a movement to the output." self.addLine( self.getLinearGcodeMovementWithFeedRate( feedRateMinute, point, z ) ) def addLine( self, line ): "Add a line of text and a newline to the output." if len( line ) <= 0: return splitLine = getSplitLineBeforeBracketSemicolon( line ) firstWord = getFirstWord( splitLine ) if firstWord == 'G90': self.absoluteDistanceMode = True elif firstWord == 'G91': self.absoluteDistanceMode = False elif firstWord == 'G1': feedRateMinute = getFeedRateMinute( None, splitLine ) if self.absoluteDistanceMode: location = getLocationFromSplitLine( self.oldAddedLocation, splitLine ) line = self.getLineWithZLimitedFeedRate( feedRateMinute, line, location, splitLine ) self.oldAddedLocation = location else: if self.oldAddedLocation == None: print( 'Warning: There was no absolute location when the G91 command was parsed, so the absolute location will be set to the origin.' ) self.oldAddedLocation = Vector3() self.oldAddedLocation += getLocationFromSplitLine( None, splitLine ) elif firstWord == 'G92': self.oldAddedLocation = getLocationFromSplitLine( self.oldAddedLocation, splitLine ) elif firstWord == 'M101': self.maximumZFeedRatePerSecond = self.maximumZDrillFeedRatePerSecond elif firstWord == 'M103': self.maximumZFeedRatePerSecond = self.maximumZTravelFeedRatePerSecond self.output.write( line + "\n" ) def addLines( self, lines ): "Add lines of text to the output." for line in lines: self.addLine( line ) def addLinesSetAbsoluteDistanceMode( self, lines ): "Add lines of text to the output." self.addLines( lines ) self.absoluteDistanceMode = True def addPerimeterBlock( self, loop, z ): "Add the perimeter gcode block for the loop." if len( loop ) < 2: return if euclidean.isWiddershins( loop ): # Indicate that a perimeter is beginning. self.addLine( '( outer )' ) else: self.addLine( '( inner )' ) self.addGcodeFromThreadZ( loop + [ loop[ 0 ] ], z ) self.addLine( '()' ) # Indicate that a perimeter is beginning. def addTagBracketedLine( self, tagName, value ): "Add a begin tag, balue and end tag." self.addLine( self.getTagBracketedLine( tagName, value ) ) def getBoundaryLine( self, location ): "Get boundary gcode line." return '( X%s Y%s Z%s )' % ( self.getRounded( location.x ), self.getRounded( location.y ), self.getRounded( location.z ) ) def getFirstWordMovement( self, firstWord, location ): "Get the start of the arc line." return '%s X%s Y%s Z%s' % ( firstWord, self.getRounded( location.x ), self.getRounded( location.y ), self.getRounded( location.z ) ) def getLinearGcodeMovement( self, point, z ): "Get a linear gcode movement." return "G1 X%s Y%s Z%s" % ( self.getRounded( point.real ), self.getRounded( point.imag ), self.getRounded( z ) ) def getLinearGcodeMovementWithFeedRate( self, feedRateMinute, point, z ): "Get a z limited gcode movement." addedLocation = Vector3( point.real, point.imag, z ) if addedLocation == self.oldAddedLocation: return '' linearGcodeMovement = self.getLinearGcodeMovement( point, z ) if feedRateMinute == None: return linearGcodeMovement return linearGcodeMovement + ' F' + self.getRounded( feedRateMinute ) def getLinearGcodeMovementWithZLimitedFeedRate( self, feedRateMinute, location ): "Get a z limited gcode movement." if location == self.oldAddedLocation: return '' distance = 0.0 extrusionDistanceString = '' if self.oldAddedLocation != None: distance = abs( location - self.oldAddedLocation ) linearGcodeMovement = self.getLinearGcodeMovement( location.dropAxis( 2 ), location.z ) if feedRateMinute == None: return linearGcodeMovement if self.oldAddedLocation != None: deltaZ = abs( location.z - self.oldAddedLocation.z ) feedRateMinute = self.getZLimitedFeedRate( deltaZ, distance, feedRateMinute ) return linearGcodeMovement + ' F' + self.getRounded( feedRateMinute ) def getLineWithFeedRate( self, feedRateMinute, line, splitLine ): "Get the line with a feed rate." roundedFeedRateString = 'F' + self.getRounded( feedRateMinute ) indexOfF = indexOfStartingWithSecond( 'F', splitLine ) if indexOfF < 0: return line + ' ' + roundedFeedRateString word = splitLine[ indexOfF ] return line.replace( word, roundedFeedRateString ) def getLineWithX( self, line, splitLine, x ): "Get the line with an x." roundedXString = 'X' + self.getRounded( x ) indexOfX = indexOfStartingWithSecond( 'X', splitLine ) if indexOfX == - 1: return line + ' ' + roundedXString word = splitLine[ indexOfX ] return line.replace( word, roundedXString ) def getLineWithY( self, line, splitLine, y ): "Get the line with a y." roundedYString = 'Y' + self.getRounded( y ) indexOfY = indexOfStartingWithSecond( 'Y', splitLine ) if indexOfY == - 1: return line + ' ' + roundedYString word = splitLine[ indexOfY ] return line.replace( word, roundedYString ) def getLineWithZ( self, line, splitLine, z ): "Get the line with a z." roundedZString = 'Z' + self.getRounded( z ) indexOfZ = indexOfStartingWithSecond( 'Z', splitLine ) if indexOfZ == - 1: return line + ' ' + roundedZString word = splitLine[ indexOfZ ] return line.replace( word, roundedZString ) def getLineWithZLimitedFeedRate( self, feedRateMinute, line, location, splitLine ): "Get a replaced limited gcode movement line." if location == self.oldAddedLocation: return '' if feedRateMinute == None: return line if self.oldAddedLocation != None: deltaZ = abs( location.z - self.oldAddedLocation.z ) distance = abs( location - self.oldAddedLocation ) feedRateMinute = self.getZLimitedFeedRate( deltaZ, distance, feedRateMinute ) return self.getLineWithFeedRate( feedRateMinute, line, splitLine ) def getRounded( self, number ): "Get number rounded to the number of carried decimal places as a string." return euclidean.getRoundedToDecimalPlacesString( self.decimalPlacesCarried, number ) def getTagBracketedLine( self, tagName, value ): "Add a begin tag, balue and end tag." return '(<%s> %s )' % ( tagName, value, tagName ) def getZLimitedFeedRate( self, deltaZ, distance, feedRateMinute ): "Get the z limited feed rate." if self.maximumZFeedRatePerSecond == None: return feedRateMinute zFeedRateSecond = feedRateMinute * deltaZ / distance / 60.0 if zFeedRateSecond > self.maximumZFeedRatePerSecond: feedRateMinute *= self.maximumZFeedRatePerSecond / zFeedRateSecond return feedRateMinute def parseSplitLine( self, firstWord, splitLine ): "Parse gcode split line and store the parameters." firstWord = getWithoutBracketsEqualTab( firstWord ) if firstWord == 'decimalPlacesCarried': self.decimalPlacesCarried = int( splitLine[ 1 ] ) elif firstWord == 'maximumZDrillFeedRatePerSecond': self.maximumZDrillFeedRatePerSecond = float( splitLine[ 1 ] ) self.maximumZFeedRatePerSecond = self.maximumZDrillFeedRatePerSecond elif firstWord == 'maximumZTravelFeedRatePerSecond': self.maximumZTravelFeedRatePerSecond = float( splitLine[ 1 ] )