PageRenderTime 36ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Program_Files/replicatorg-0025/skein_engines/skeinforge-0006/skeinforge_tools/analyze_plugins/statistic.py

https://github.com/sialan/autonomous-sprayer
Python | 297 lines | 288 code | 6 blank | 3 comment | 3 complexity | 1a6e8ee02f49aa127aceab821f699020 MD5 | raw file
  1. """
  2. Statistic is a script to generate statistics a gcode file.
  3. The default 'Activate Statistic' checkbox is on. When it is on, the functions described below will work when called from the
  4. skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether
  5. or not the 'Activate Statistic' checkbox is on, when statistic is run directly.
  6. When the 'Print Statistics' checkbox is on, the statistics will be printed to the console, the default is on. When the 'Save
  7. Statistics' checkbox is on, the statistics will be save as a .txt file, the default is off.
  8. To run statistic, in a shell in the folder which statistic is in type:
  9. > python statistic.py
  10. An explanation of the gcodes is at:
  11. http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
  12. and at:
  13. http://reprap.org/bin/view/Main/MCodeReference
  14. A gode example is at:
  15. http://forums.reprap.org/file.php?12,file=565
  16. This example generates statistics the gcode file Screw Holder_comb.gcode. This example is run in a terminal in the folder which contains
  17. Screw Holder_comb.gcode and statistic.py.
  18. > python
  19. Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31)
  20. [GCC 4.2.1 (SUSE Linux)] on linux2
  21. Type "help", "copyright", "credits" or "license" for more information.
  22. >>> import statistic
  23. >>> statistic.main()
  24. This brings up the statistic dialog.
  25. >>> statistic.statisticFile()
  26. The statistics file is saved as Screw Holder_comb_statistic.gcode
  27. """
  28. from __future__ import absolute_import
  29. #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.
  30. import __init__
  31. from skeinforge_tools.skeinforge_utilities.vector3 import Vector3
  32. from skeinforge_tools.skeinforge_utilities import euclidean
  33. from skeinforge_tools.skeinforge_utilities import gcodec
  34. from skeinforge_tools.skeinforge_utilities import preferences
  35. from skeinforge_tools import polyfile
  36. import cStringIO
  37. import math
  38. import sys
  39. __author__ = "Enrique Perez (perez_enrique@yahoo.com)"
  40. __date__ = "$Date: 2008/21/04 $"
  41. __license__ = "GPL 3.0"
  42. def getStatisticGcode( gcodeText ):
  43. "Get statistics for a gcode text."
  44. skein = StatisticSkein()
  45. skein.parseGcode( gcodeText )
  46. return skein.output.getvalue()
  47. def statisticFile( fileName = '' ):
  48. "Write statistics for a gcode file. If no fileName is specified, write statistics for the first gcode file in this folder that is not modified."
  49. if fileName == '':
  50. unmodified = gcodec.getUnmodifiedGCodeFiles()
  51. if len( unmodified ) == 0:
  52. print( "There are no unmodified gcode files in this folder." )
  53. return
  54. fileName = unmodified[ 0 ]
  55. statisticPreferences = StatisticPreferences()
  56. preferences.readPreferences( statisticPreferences )
  57. writeStatisticFileGivenText( fileName, gcodec.getFileText( fileName ), statisticPreferences )
  58. def writeOutput( fileName, gcodeText = '' ):
  59. "Write statistics for a skeinforge gcode file, if 'Write Statistics File for Skeinforge Chain' is selected."
  60. statisticPreferences = StatisticPreferences()
  61. preferences.readPreferences( statisticPreferences )
  62. if gcodeText == '':
  63. gcodeText = gcodec.getFileText( fileName )
  64. if statisticPreferences.activateStatistic.value:
  65. writeStatisticFileGivenText( fileName, gcodeText, statisticPreferences )
  66. def writeStatisticFileGivenText( fileName, gcodeText, statisticPreferences ):
  67. "Write statistics for a gcode file."
  68. print( 'Statistics are being generated for the file ' + gcodec.getSummarizedFilename( fileName ) )
  69. statisticGcode = getStatisticGcode( gcodeText )
  70. if statisticPreferences.printStatistics.value:
  71. print( statisticGcode )
  72. if statisticPreferences.saveStatistics.value:
  73. gcodec.writeFileMessageEnd( '.txt', fileName, statisticGcode, 'The statistics file is saved as ' )
  74. class StatisticPreferences:
  75. "A class to handle the statistics preferences."
  76. def __init__( self ):
  77. "Set the default preferences, execute title & preferences fileName."
  78. #Set the default preferences.
  79. self.archive = []
  80. self.activateStatistic = preferences.BooleanPreference().getFromValue( 'Activate Statistic', True )
  81. self.archive.append( self.activateStatistic )
  82. self.fileNameInput = preferences.Filename().getFromFilename( [ ( 'Gcode text files', '*.gcode' ) ], 'Open File to Generate Statistics for', '' )
  83. self.archive.append( self.fileNameInput )
  84. self.printStatistics = preferences.BooleanPreference().getFromValue( 'Print Statistics', True )
  85. self.archive.append( self.printStatistics )
  86. self.saveStatistics = preferences.BooleanPreference().getFromValue( 'Save Statistics', False )
  87. self.archive.append( self.saveStatistics )
  88. #Create the archive, title of the execute button, title of the dialog & preferences fileName.
  89. self.executeTitle = 'Generate Statistics'
  90. self.saveTitle = 'Save Preferences'
  91. preferences.setHelpPreferencesFileNameTitleWindowPosition( self, 'skeinforge_tools.analyze_plugins.statistic.html' )
  92. def execute( self ):
  93. "Write button has been clicked."
  94. fileNames = polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled, [ '_comment' ] )
  95. for fileName in fileNames:
  96. statisticFile( fileName )
  97. class StatisticSkein:
  98. "A class to get statistics for a gcode skein."
  99. def __init__( self ):
  100. self.oldLocation = None
  101. self.output = cStringIO.StringIO()
  102. def addLine( self, line ):
  103. "Add a line of text and a newline to the output."
  104. self.output.write( line + "\n" )
  105. def addToPath( self, location ):
  106. "Add a point to travel and maybe extrusion."
  107. if self.oldLocation != None:
  108. travel = location.distance( self.oldLocation )
  109. if self.feedrateMinute > 0.0:
  110. self.totalBuildTime += 60.0 * travel / self.feedrateMinute
  111. self.totalDistanceTraveled += travel
  112. if self.extruderActive:
  113. self.totalDistanceExtruded += travel
  114. self.cornerHigh = euclidean.getPointMaximum( self.cornerHigh, location )
  115. self.cornerLow = euclidean.getPointMinimum( self.cornerLow, location )
  116. self.oldLocation = location
  117. def extruderSet( self, active ):
  118. "Maybe increment the number of times the extruder was toggled."
  119. if self.extruderActive != active:
  120. self.extruderToggled += 1
  121. self.extruderActive = active
  122. def getLocationSetFeedrateToSplitLine( self, splitLine ):
  123. location = gcodec.getLocationFromSplitLine( self.oldLocation, splitLine )
  124. indexOfF = gcodec.indexOfStartingWithSecond( "F", splitLine )
  125. if indexOfF > 0:
  126. self.feedrateMinute = gcodec.getDoubleAfterFirstLetter( splitLine[ indexOfF ] )
  127. return location
  128. def helicalMove( self, isCounterclockwise, splitLine ):
  129. "Get statistics for a helical move."
  130. if self.oldLocation == None:
  131. return
  132. location = self.getLocationSetFeedrateToSplitLine( splitLine )
  133. location += self.oldLocation
  134. center = self.oldLocation.copy()
  135. indexOfR = gcodec.indexOfStartingWithSecond( "R", splitLine )
  136. if indexOfR > 0:
  137. radius = gcodec.getDoubleAfterFirstLetter( splitLine[ indexOfR ] )
  138. halfLocationMinusOld = location - self.oldLocation
  139. halfLocationMinusOld *= 0.5
  140. halfLocationMinusOldLength = halfLocationMinusOld.magnitude()
  141. centerMidpointDistance = math.sqrt( radius * radius - halfLocationMinusOldLength * halfLocationMinusOldLength )
  142. centerMinusMidpoint = euclidean.getRotatedWiddershinsQuarterAroundZAxis( halfLocationMinusOld )
  143. centerMinusMidpoint.normalize()
  144. centerMinusMidpoint *= centerMidpointDistance
  145. if isCounterclockwise:
  146. center.setToVec3( halfLocationMinusOld + centerMinusMidpoint )
  147. else:
  148. center.setToVec3( halfLocationMinusOld - centerMinusMidpoint )
  149. else:
  150. center.x = gcodec.getDoubleForLetter( "I", splitLine )
  151. center.y = gcodec.getDoubleForLetter( "J", splitLine )
  152. curveSection = 0.5
  153. center += self.oldLocation
  154. afterCenterSegment = location - center
  155. beforeCenterSegment = self.oldLocation - center
  156. afterCenterDifferenceAngle = euclidean.getAngleAroundZAxisDifference( afterCenterSegment, beforeCenterSegment )
  157. absoluteDifferenceAngle = abs( afterCenterDifferenceAngle )
  158. steps = int( round( 0.5 + max( absoluteDifferenceAngle * 2.4, absoluteDifferenceAngle * beforeCenterSegment.magnitude() / curveSection ) ) )
  159. stepPlaneAngle = euclidean.getPolar( afterCenterDifferenceAngle / steps, 1.0 )
  160. zIncrement = ( afterCenterSegment.z - beforeCenterSegment.z ) / float( steps )
  161. for step in xrange( 1, steps ):
  162. beforeCenterSegment = euclidean.getRoundZAxisByPlaneAngle( stepPlaneAngle, beforeCenterSegment )
  163. beforeCenterSegment.z += zIncrement
  164. arcPoint = center + beforeCenterSegment
  165. self.addToPath( arcPoint )
  166. self.addToPath( location )
  167. def linearMove( self, splitLine ):
  168. "Get statistics for a linear move."
  169. location = self.getLocationSetFeedrateToSplitLine( splitLine )
  170. self.addToPath( location )
  171. def parseGcode( self, gcodeText ):
  172. "Parse gcode text and store the statistics."
  173. self.characters = 0
  174. self.cornerHigh = Vector3( - 999999999.0, - 999999999.0, - 999999999.0 )
  175. self.cornerLow = Vector3( 999999999.0, 999999999.0, 999999999.0 )
  176. self.extruderActive = False
  177. self.extruderSpeed = 0.0
  178. self.extruderToggled = 0
  179. self.extrusionDiameter = None
  180. self.extrusionWidth = 0.4
  181. self.feedrateMinute = 600.0
  182. self.layerThickness = 0.4
  183. self.numberOfLines = 0
  184. self.procedures = []
  185. self.totalBuildTime = 0.0
  186. self.totalDistanceExtruded = 0.0
  187. self.totalDistanceTraveled = 0.0
  188. lines = gcodec.getTextLines( gcodeText )
  189. for line in lines:
  190. self.parseLine( line )
  191. averageFeedrate = self.totalDistanceTraveled / self.totalBuildTime
  192. self.characters += self.numberOfLines
  193. kilobytes = round( self.characters / 1024.0 )
  194. halfExtrusionWidth = 0.5 * self.extrusionWidth
  195. halfExtrusionCorner = Vector3( halfExtrusionWidth, halfExtrusionWidth, halfExtrusionWidth )
  196. self.cornerHigh += halfExtrusionCorner
  197. self.cornerLow -= halfExtrusionCorner
  198. extent = self.cornerHigh - self.cornerLow
  199. roundedHigh = euclidean.getRoundedPoint( self.cornerHigh )
  200. roundedLow = euclidean.getRoundedPoint( self.cornerLow )
  201. roundedExtent = euclidean.getRoundedPoint( extent )
  202. axisString = " axis, the extrusion starts at "
  203. volumeExtruded = 0.0009 * self.extrusionWidth * self.layerThickness * self.totalDistanceExtruded # the 9 comes from a typical fill density of 0.9
  204. self.addLine( "On the X" + axisString + str( int ( roundedLow.x ) ) + " mm and ends at " + str( int ( roundedHigh.x ) ) + " mm, for a width of " + str( int ( extent.x ) ) + " mm" )
  205. self.addLine( "On the Y" + axisString + str( int ( roundedLow.y ) ) + " mm and ends at " + str( int ( roundedHigh.y ) ) + " mm, for a depth of " + str( int ( extent.y ) ) + " mm" )
  206. self.addLine( "On the Z" + axisString + str( int ( roundedLow.z ) ) + " mm and ends at " + str( int ( roundedHigh.z ) ) + " mm, for a height of " + str( int ( extent.z ) ) + " mm" )
  207. self.addLine( "The average feedrate is " + str( int( round( averageFeedrate ) ) ) + " mm/s, (" + str( int( round( 60.0 * averageFeedrate ) ) ) + " mm/min)." )
  208. self.addLine( "The extruder speed is " + str( int( round( self.extruderSpeed ) ) ) )
  209. self.addLine( "The extruder was extruding " + str( int( round( 100.0 * self.totalDistanceExtruded / self.totalDistanceTraveled ) ) ) + "% of the time." )
  210. self.addLine( "The extruder was toggled " + str( self.extruderToggled ) + " times." )
  211. if self.extrusionDiameter != None:
  212. self.addLine( "The extrusion diameter is " + str( self.extrusionDiameter ) + " mm." )
  213. self.addLine( "The extrusion width is " + str( self.extrusionWidth ) + " mm." )
  214. self.addLine( "The following procedures have been performed on the skein:" )
  215. for procedure in self.procedures:
  216. self.addLine( procedure )
  217. self.addLine( "The layer thickness is " + str( self.layerThickness ) + " mm." )
  218. self.addLine( "The text has " + str( self.numberOfLines ) + " lines and a size of " + str( kilobytes ) + " KB." )
  219. self.addLine( "The total build time is " + str( int( round( self.totalBuildTime ) ) ) + " s." )
  220. self.addLine( "The total distance extruded is " + str( int( round( self.totalDistanceExtruded ) ) ) + " mm." )
  221. self.addLine( "The total distance traveled is " + str( int( round( self.totalDistanceTraveled ) ) ) + " mm." )
  222. self.addLine( "The volume extruded is " + str( int( round( volumeExtruded ) ) ) + " cc." )
  223. def parseLine( self, line ):
  224. "Parse a gcode line and add it to the statistics."
  225. self.characters += len( line )
  226. self.numberOfLines += 1
  227. splitLine = line.split()
  228. if len( splitLine ) < 1:
  229. return
  230. firstWord = splitLine[ 0 ]
  231. if firstWord == 'G1':
  232. self.linearMove( splitLine )
  233. elif firstWord == 'G2':
  234. self.helicalMove( False, splitLine )
  235. elif firstWord == 'G3':
  236. self.helicalMove( True, splitLine )
  237. elif firstWord == 'M101':
  238. self.extruderSet( True )
  239. elif firstWord == 'M102':
  240. self.extruderSet( False )
  241. elif firstWord == 'M103':
  242. self.extruderSet( False )
  243. elif firstWord == 'M108':
  244. self.extruderSpeed = gcodec.getDoubleAfterFirstLetter( splitLine[ 1 ] )
  245. elif firstWord == '(<extrusionDiameter>':
  246. self.extrusionDiameter = gcodec.getDoubleAfterFirstLetter( splitLine[ 1 ] )
  247. elif firstWord == '(<extrusionWidth>':
  248. self.extrusionWidth = gcodec.getDoubleAfterFirstLetter( splitLine[ 1 ] )
  249. elif firstWord == '(<layerThickness>':
  250. self.layerThickness = gcodec.getDoubleAfterFirstLetter( splitLine[ 1 ] )
  251. elif firstWord == '(<procedureDone>':
  252. self.procedures.append( splitLine[ 1 ] )
  253. def main():
  254. "Display the statistics dialog."
  255. if len( sys.argv ) > 1:
  256. writeOutput( ' '.join( sys.argv[ 1 : ] ) )
  257. else:
  258. preferences.displayDialog( StatisticPreferences() )
  259. if __name__ == "__main__":
  260. main()