PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Program_Files/replicatorg-0025/skein_engines/skeinforge-35/skeinforge_application/skeinforge_plugins/analyze_plugins/statistic.py

https://github.com/sialan/autonomous-sprayer
Python | 360 lines | 323 code | 26 blank | 11 comment | 12 complexity | 622c37929acf979120a9ef0a932bf55f MD5 | raw file
  1. """
  2. This page is in the table of contents.
  3. Statistic is a script to generate statistics a gcode file.
  4. The statistic manual page is at:
  5. http://www.bitsfrombytes.com/wiki/index.php?title=Skeinforge_Statistic
  6. ==Operation==
  7. The default 'Activate Statistic' checkbox is on. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Statistic' checkbox is on, when statistic is run directly.
  8. ==Settings==
  9. ===Extrusion Diameter over Thickness===
  10. Default is 1.25.
  11. The 'Extrusion Diameter over Thickness is the ratio of the extrusion diameter over the layer thickness, the default is 1.25. The extrusion fill density ratio that is printed to the console, ( it is derived quantity not a parameter ) is the area of the extrusion diameter over the extrusion width over the layer thickness. Assuming the extrusion diameter is correct, a high value means the filament will be packed tightly, and the object will be almost as dense as the filament. If the fill density ratio is too high, there could be too little room for the filament, and the extruder will end up plowing through the extra filament. A low fill density ratio means the filaments will be far away from each other, the object will be leaky and light. The fill density ratio with the default extrusion settings is around 0.68.
  12. ===Print Statistics===
  13. Default is on.
  14. When the 'Print Statistics' checkbox is on, the statistics will be printed to the console.
  15. ===Save Statistics===
  16. Default is off.
  17. When the 'Save Statistics' checkbox is on, the statistics will be saved as a .txt file.
  18. ==Gcodes==
  19. An explanation of the gcodes is at:
  20. http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
  21. and at:
  22. http://reprap.org/bin/view/Main/MCodeReference
  23. A gode example is at:
  24. http://forums.reprap.org/file.php?12,file=565
  25. ==Examples==
  26. Below are examples of statistic being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and statistic.py. The 'Save Statistics' checkbox is selected.
  27. > python statistic.py
  28. This brings up the statistic dialog.
  29. > python statistic.py Screw Holder_penultimate.gcode
  30. The statistic file is saved as Screw_Holder_penultimate_statistic.txt
  31. > python
  32. Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31)
  33. [GCC 4.2.1 (SUSE Linux)] on linux2
  34. Type "help", "copyright", "credits" or "license" for more information.
  35. >>> import statistic
  36. >>> statistic.main()
  37. This brings up the statistic dialog.
  38. >>> statistic.getWindowAnalyzeFile('Screw Holder_penultimate.gcode')
  39. The statistics file is saved as Screw Holder_penultimate_statistic.txt
  40. """
  41. from __future__ import absolute_import
  42. #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.
  43. import __init__
  44. from fabmetheus_utilities.vector3 import Vector3
  45. from fabmetheus_utilities import archive
  46. from fabmetheus_utilities import euclidean
  47. from fabmetheus_utilities import gcodec
  48. from fabmetheus_utilities import settings
  49. from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
  50. import cStringIO
  51. import math
  52. import sys
  53. __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
  54. __date__ = '$Date: 2008/21/04 $'
  55. __license__ = 'GPL 3.0'
  56. def getNewRepository():
  57. "Get the repository constructor."
  58. return StatisticRepository()
  59. def getWindowAnalyzeFile(fileName):
  60. "Write statistics for a gcode file."
  61. return getWindowAnalyzeFileGivenText( fileName, archive.getFileText(fileName) )
  62. def getWindowAnalyzeFileGivenText( fileName, gcodeText, repository=None):
  63. "Write statistics for a gcode file."
  64. print('')
  65. print('')
  66. print('Statistics are being generated for the file ' + archive.getSummarizedFileName(fileName) )
  67. if repository == None:
  68. repository = settings.getReadRepository( StatisticRepository() )
  69. skein = StatisticSkein()
  70. statisticGcode = skein.getCraftedGcode(gcodeText, repository)
  71. if repository.printStatistics.value:
  72. print( statisticGcode )
  73. if repository.saveStatistics.value:
  74. archive.writeFileMessageEnd('.txt', fileName, statisticGcode, 'The statistics file is saved as ')
  75. def writeOutput( fileName, fileNameSuffix, gcodeText = ''):
  76. "Write statistics for a skeinforge gcode file, if 'Write Statistics File for Skeinforge Chain' is selected."
  77. repository = settings.getReadRepository( StatisticRepository() )
  78. if gcodeText == '':
  79. gcodeText = archive.getFileText( fileNameSuffix )
  80. if repository.activateStatistic.value:
  81. getWindowAnalyzeFileGivenText( fileNameSuffix, gcodeText, repository )
  82. class StatisticRepository:
  83. "A class to handle the statistics settings."
  84. def __init__(self):
  85. "Set the default settings, execute title & settings fileName."
  86. settings.addListsToRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.statistic.html', None, self )
  87. self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://www.bitsfrombytes.com/wiki/index.php?title=Skeinforge_Statistic')
  88. self.activateStatistic = settings.BooleanSetting().getFromValue('Activate Statistic', self, True )
  89. settings.LabelSeparator().getFromRepository(self)
  90. settings.LabelDisplay().getFromName('- Cost -', self )
  91. self.machineTime = settings.FloatSpin().getFromValue( 0.0, 'Machine Time ($/hour):', self, 2.0, 1.0 )
  92. self.material = settings.FloatSpin().getFromValue( 0.0, 'Material ($/kg):', self, 20.0, 10.0 )
  93. settings.LabelSeparator().getFromRepository(self)
  94. self.density = settings.FloatSpin().getFromValue( 500.0, 'Density (kg/m3):', self, 2000.0, 930.0 )
  95. self.extrusionDiameterOverThickness = settings.FloatSpin().getFromValue( 1.0, 'Extrusion Diameter over Thickness (ratio):', self, 1.5, 1.25 )
  96. self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File to Generate Statistics for', self, '')
  97. self.printStatistics = settings.BooleanSetting().getFromValue('Print Statistics', self, True )
  98. self.saveStatistics = settings.BooleanSetting().getFromValue('Save Statistics', self, False )
  99. self.executeTitle = 'Generate Statistics'
  100. def execute(self):
  101. "Write button has been clicked."
  102. fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled, ['_comment'] )
  103. for fileName in fileNames:
  104. getWindowAnalyzeFile(fileName)
  105. class StatisticSkein:
  106. "A class to get statistics for a gcode skein."
  107. def __init__(self):
  108. self.extrusionDiameter = None
  109. self.oldLocation = None
  110. self.operatingFeedRatePerSecond = None
  111. self.output = cStringIO.StringIO()
  112. self.version = None
  113. def addLine(self, line):
  114. "Add a line of text and a newline to the output."
  115. self.output.write(line + '\n')
  116. def addToPath(self, location):
  117. "Add a point to travel and maybe extrusion."
  118. if self.oldLocation != None:
  119. travel = location.distance( self.oldLocation )
  120. if self.feedRateMinute > 0.0:
  121. self.totalBuildTime += 60.0 * travel / self.feedRateMinute
  122. self.totalDistanceTraveled += travel
  123. if self.extruderActive:
  124. self.totalDistanceExtruded += travel
  125. self.cornerHigh = euclidean.getPointMaximum( self.cornerHigh, location )
  126. self.cornerLow = euclidean.getPointMinimum( self.cornerLow, location )
  127. self.oldLocation = location
  128. def extruderSet( self, active ):
  129. "Maybe increment the number of times the extruder was toggled."
  130. if self.extruderActive != active:
  131. self.extruderToggled += 1
  132. self.extruderActive = active
  133. def getCraftedGcode(self, gcodeText, repository):
  134. "Parse gcode text and store the statistics."
  135. self.absolutePerimeterWidth = 0.4
  136. self.characters = 0
  137. self.cornerHigh = Vector3(-999999999.0, -999999999.0, -999999999.0)
  138. self.cornerLow = Vector3(999999999.0, 999999999.0, 999999999.0)
  139. self.extruderActive = False
  140. self.extruderSpeed = None
  141. self.extruderToggled = 0
  142. self.feedRateMinute = 600.0
  143. self.layerThickness = 0.4
  144. self.numberOfLines = 0
  145. self.procedures = []
  146. self.repository = repository
  147. self.totalBuildTime = 0.0
  148. self.totalDistanceExtruded = 0.0
  149. self.totalDistanceTraveled = 0.0
  150. lines = archive.getTextLines(gcodeText)
  151. for line in lines:
  152. self.parseLine(line)
  153. averageFeedRate = self.totalDistanceTraveled / self.totalBuildTime
  154. self.characters += self.numberOfLines
  155. kilobytes = round( self.characters / 1024.0 )
  156. halfPerimeterWidth = 0.5 * self.absolutePerimeterWidth
  157. halfExtrusionCorner = Vector3( halfPerimeterWidth, halfPerimeterWidth, halfPerimeterWidth )
  158. self.cornerHigh += halfExtrusionCorner
  159. self.cornerLow -= halfExtrusionCorner
  160. extent = self.cornerHigh - self.cornerLow
  161. roundedHigh = euclidean.getRoundedPoint( self.cornerHigh )
  162. roundedLow = euclidean.getRoundedPoint( self.cornerLow )
  163. roundedExtent = euclidean.getRoundedPoint( extent )
  164. axisString = " axis extrusion starts at "
  165. crossSectionArea = 0.9 * self.absolutePerimeterWidth * self.layerThickness # 0.9 if from the typical fill density
  166. if self.extrusionDiameter != None:
  167. crossSectionArea = math.pi / 4.0 * self.extrusionDiameter * self.extrusionDiameter
  168. volumeExtruded = 0.001 * crossSectionArea * self.totalDistanceExtruded
  169. mass = volumeExtruded / repository.density.value
  170. machineTimeCost = repository.machineTime.value * self.totalBuildTime / 3600.0
  171. materialCost = repository.material.value * mass
  172. self.addLine(' ')
  173. self.addLine('Cost')
  174. self.addLine( "Machine time cost is %s$." % round( machineTimeCost, 2 ) )
  175. self.addLine( "Material cost is %s$." % round( materialCost, 2 ) )
  176. self.addLine( "Total cost is %s$." % round( machineTimeCost + materialCost, 2 ) )
  177. self.addLine(' ')
  178. self.addLine('Extent')
  179. self.addLine( "X%s%s mm and ends at %s mm, for a width of %s mm." % ( axisString, int( roundedLow.x ), int( roundedHigh.x ), int( extent.x ) ) )
  180. self.addLine( "Y%s%s mm and ends at %s mm, for a depth of %s mm." % ( axisString, int( roundedLow.y ), int( roundedHigh.y ), int( extent.y ) ) )
  181. self.addLine( "Z%s%s mm and ends at %s mm, for a height of %s mm." % ( axisString, int( roundedLow.z ), int( roundedHigh.z ), int( extent.z ) ) )
  182. self.addLine(' ')
  183. self.addLine('Extruder')
  184. self.addLine( "Build time is %s." % euclidean.getDurationString( self.totalBuildTime ) )
  185. self.addLine( "Distance extruded is %s mm." % euclidean.getThreeSignificantFigures( self.totalDistanceExtruded ) )
  186. self.addLine( "Distance traveled is %s mm." % euclidean.getThreeSignificantFigures( self.totalDistanceTraveled ) )
  187. if self.extruderSpeed != None:
  188. self.addLine( "Extruder speed is %s" % euclidean.getThreeSignificantFigures( self.extruderSpeed ) )
  189. self.addLine( "Extruder was extruding %s percent of the time." % euclidean.getThreeSignificantFigures( 100.0 * self.totalDistanceExtruded / self.totalDistanceTraveled ) )
  190. self.addLine( "Extruder was toggled %s times." % self.extruderToggled )
  191. if self.operatingFeedRatePerSecond != None:
  192. flowRate = crossSectionArea * self.operatingFeedRatePerSecond
  193. self.addLine( "Operating flow rate is %s mm3/s." % euclidean.getThreeSignificantFigures( flowRate ) )
  194. self.addLine( "Feed rate average is %s mm/s, (%s mm/min)." % ( euclidean.getThreeSignificantFigures( averageFeedRate ), euclidean.getThreeSignificantFigures( 60.0 * averageFeedRate ) ) )
  195. self.addLine(' ')
  196. self.addLine('Filament')
  197. self.addLine( "Cross section area is %s mm2." % euclidean.getThreeSignificantFigures( crossSectionArea ) )
  198. if self.extrusionDiameter != None:
  199. self.addLine( "Extrusion diameter is %s mm." % euclidean.getThreeSignificantFigures( self.extrusionDiameter ) )
  200. self.addLine('Extrusion fill density ratio is %s' % euclidean.getThreeSignificantFigures( crossSectionArea / self.absolutePerimeterWidth / self.layerThickness ) )
  201. self.addLine(' ')
  202. self.addLine('Material')
  203. self.addLine( "Mass extruded is %s grams." % euclidean.getThreeSignificantFigures( 1000.0 * mass ) )
  204. self.addLine( "Volume extruded is %s cc." % euclidean.getThreeSignificantFigures( volumeExtruded ) )
  205. self.addLine(' ')
  206. self.addLine('Meta')
  207. self.addLine( "Text has %s lines and a size of %s KB." % ( self.numberOfLines, kilobytes ) )
  208. if self.version != None:
  209. self.addLine( "Version is " + self.version )
  210. self.addLine(' ')
  211. self.addLine( "Procedures" )
  212. for procedure in self.procedures:
  213. self.addLine(procedure)
  214. self.addLine(' ')
  215. self.addLine('Slice')
  216. self.addLine( "Layer thickness is %s mm." % euclidean.getThreeSignificantFigures( self.layerThickness ) )
  217. self.addLine( "Perimeter width is %s mm." % euclidean.getThreeSignificantFigures( self.absolutePerimeterWidth ) )
  218. self.addLine(' ')
  219. return self.output.getvalue()
  220. def getLocationSetFeedRateToSplitLine( self, splitLine ):
  221. "Get location ans set feed rate to the plsit line."
  222. location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine)
  223. indexOfF = gcodec.getIndexOfStartingWithSecond( "F", splitLine )
  224. if indexOfF > 0:
  225. self.feedRateMinute = gcodec.getDoubleAfterFirstLetter( splitLine[indexOfF] )
  226. return location
  227. def helicalMove( self, isCounterclockwise, splitLine ):
  228. "Get statistics for a helical move."
  229. if self.oldLocation == None:
  230. return
  231. location = self.getLocationSetFeedRateToSplitLine(splitLine)
  232. location += self.oldLocation
  233. center = self.oldLocation.copy()
  234. indexOfR = gcodec.getIndexOfStartingWithSecond( "R", splitLine )
  235. if indexOfR > 0:
  236. radius = gcodec.getDoubleAfterFirstLetter( splitLine[ indexOfR ] )
  237. halfLocationMinusOld = location - self.oldLocation
  238. halfLocationMinusOld *= 0.5
  239. halfLocationMinusOldLength = halfLocationMinusOld.magnitude()
  240. centerMidpointDistanceSquared = radius * radius - halfLocationMinusOldLength * halfLocationMinusOldLength
  241. centerMidpointDistance = math.sqrt( max( centerMidpointDistanceSquared, 0.0 ) )
  242. centerMinusMidpoint = euclidean.getRotatedWiddershinsQuarterAroundZAxis( halfLocationMinusOld )
  243. centerMinusMidpoint.normalize()
  244. centerMinusMidpoint *= centerMidpointDistance
  245. if isCounterclockwise:
  246. center.setToVector3( halfLocationMinusOld + centerMinusMidpoint )
  247. else:
  248. center.setToVector3( halfLocationMinusOld - centerMinusMidpoint )
  249. else:
  250. center.x = gcodec.getDoubleForLetter( "I", splitLine )
  251. center.y = gcodec.getDoubleForLetter( "J", splitLine )
  252. curveSection = 0.5
  253. center += self.oldLocation
  254. afterCenterSegment = location - center
  255. beforeCenterSegment = self.oldLocation - center
  256. afterCenterDifferenceAngle = euclidean.getAngleAroundZAxisDifference( afterCenterSegment, beforeCenterSegment )
  257. absoluteDifferenceAngle = abs( afterCenterDifferenceAngle )
  258. steps = int( round( 0.5 + max( absoluteDifferenceAngle * 2.4, absoluteDifferenceAngle * beforeCenterSegment.magnitude() / curveSection ) ) )
  259. stepPlaneAngle = euclidean.getWiddershinsUnitPolar( afterCenterDifferenceAngle / steps )
  260. zIncrement = ( afterCenterSegment.z - beforeCenterSegment.z ) / float( steps )
  261. for step in xrange( 1, steps ):
  262. beforeCenterSegment = euclidean.getRoundZAxisByPlaneAngle( stepPlaneAngle, beforeCenterSegment )
  263. beforeCenterSegment.z += zIncrement
  264. arcPoint = center + beforeCenterSegment
  265. self.addToPath( arcPoint )
  266. self.addToPath( location )
  267. def linearMove( self, splitLine ):
  268. "Get statistics for a linear move."
  269. location = self.getLocationSetFeedRateToSplitLine(splitLine)
  270. self.addToPath( location )
  271. def parseLine(self, line):
  272. "Parse a gcode line and add it to the statistics."
  273. self.characters += len(line)
  274. self.numberOfLines += 1
  275. splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line)
  276. if len(splitLine) < 1:
  277. return
  278. firstWord = splitLine[0]
  279. if firstWord == 'G1':
  280. self.linearMove(splitLine)
  281. elif firstWord == 'G2':
  282. self.helicalMove( False, splitLine )
  283. elif firstWord == 'G3':
  284. self.helicalMove( True, splitLine )
  285. elif firstWord == 'M101':
  286. self.extruderSet( True )
  287. elif firstWord == 'M102':
  288. self.extruderSet( False )
  289. elif firstWord == 'M103':
  290. self.extruderSet( False )
  291. elif firstWord == 'M108':
  292. self.extruderSpeed = gcodec.getDoubleAfterFirstLetter(splitLine[1])
  293. elif firstWord == '(<layerThickness>':
  294. self.layerThickness = float(splitLine[1])
  295. self.extrusionDiameter = self.repository.extrusionDiameterOverThickness.value * self.layerThickness
  296. elif firstWord == '(<operatingFeedRatePerSecond>':
  297. self.operatingFeedRatePerSecond = float(splitLine[1])
  298. elif firstWord == '(<perimeterWidth>':
  299. self.absolutePerimeterWidth = abs(float(splitLine[1]))
  300. elif firstWord == '(<procedureDone>':
  301. self.procedures.append(splitLine[1])
  302. elif firstWord == '(<version>':
  303. self.version = splitLine[1]
  304. def main():
  305. "Display the statistics dialog."
  306. if len(sys.argv) > 1:
  307. getWindowAnalyzeFile(' '.join(sys.argv[1 :]))
  308. else:
  309. settings.startMainLoopFromConstructor( getNewRepository() )
  310. if __name__ == "__main__":
  311. main()