DisplayProgressOnLCD.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. # Cura PostProcessingPlugin
  2. # Author: Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen
  3. # Date: July 31, 2019
  4. # Modified: Okt 22, 2020
  5. # Description: This plugin displays progress on the LCD. It can output the estimated time remaining and the completion percentage.
  6. from ..Script import Script
  7. import re
  8. import datetime
  9. class DisplayProgressOnLCD(Script):
  10. def __init__(self):
  11. super().__init__()
  12. def getSettingDataString(self):
  13. return """{
  14. "name": "Display Progress On LCD",
  15. "key": "DisplayProgressOnLCD",
  16. "metadata": {},
  17. "version": 2,
  18. "settings":
  19. {
  20. "time_remaining":
  21. {
  22. "label": "Time Remaining",
  23. "description": "Select to write remaining time to the display.Select to write remaining time on the display using M117 status line message (almost all printers) or using M73 command (Prusa and Marlin 2 if enabled).",
  24. "type": "bool",
  25. "default_value": false
  26. },
  27. "time_remaining_method":
  28. {
  29. "label": "Time Reporting Method",
  30. "description": "How should remaining time be shown on the display? It could use a generic message command (M117, almost all printers), or a specialised time remaining command (M73, Prusa and Marlin 2).",
  31. "type": "enum",
  32. "options": {
  33. "m117":"M117 - All printers",
  34. "m73":"M73 - Prusa, Marlin 2"
  35. },
  36. "enabled": "time_remaining",
  37. "default_value": "m117"
  38. },
  39. "update_frequency":
  40. {
  41. "label": "Update frequency",
  42. "description": "Update remaining time for every layer or periodically every minute or faster.",
  43. "type": "enum",
  44. "options": {"0":"Every layer","15":"Every 15 seconds","30":"Every 30 seconds","60":"Every minute"},
  45. "default_value": "0",
  46. "enabled": "time_remaining"
  47. },
  48. "percentage":
  49. {
  50. "label": "Percentage",
  51. "description": "When enabled, set the completion bar percentage on the LCD using Marlin's M73 command.",
  52. "type": "bool",
  53. "default_value": false
  54. }
  55. }
  56. }"""
  57. # Get the time value from a line as a float.
  58. # Example line ;TIME_ELAPSED:1234.6789 or ;TIME:1337
  59. def getTimeValue(self, line):
  60. list_split = re.split(":", line) # Split at ":" so we can get the numerical value
  61. return float(list_split[1]) # Convert the numerical portion to a float
  62. def outputTime(self, lines, line_index, time_left, mode):
  63. # Do some math to get the time left in seconds into the right format. (HH,MM,SS)
  64. time_left = max(time_left, 0)
  65. m, s = divmod(time_left, 60)
  66. h, m = divmod(m, 60)
  67. # Create the string
  68. if mode == "m117":
  69. current_time_string = "{:d}h{:02d}m{:02d}s".format(int(h), int(m), int(s))
  70. # And now insert that into the GCODE
  71. lines.insert(line_index, "M117 Time Left {}".format(current_time_string))
  72. else:
  73. mins = int(60 * h + m + s / 30)
  74. lines.insert(line_index, "M73 R{}".format(mins))
  75. def execute(self, data):
  76. output_time = self.getSettingValueByKey("time_remaining")
  77. output_time_method = self.getSettingValueByKey("time_remaining_method")
  78. output_frequency = int(self.getSettingValueByKey("update_frequency"))
  79. output_percentage = self.getSettingValueByKey("percentage")
  80. line_set = {}
  81. if output_percentage or output_time:
  82. total_time = -1
  83. previous_layer_end_percentage = 0
  84. previous_layer_end_time = 0
  85. for layer in data:
  86. layer_index = data.index(layer)
  87. lines = layer.split("\n")
  88. for line in lines:
  89. if (line.startswith(";TIME:") or line.startswith(";PRINT.TIME:")) and total_time == -1:
  90. # This line represents the total time required to print the gcode
  91. total_time = self.getTimeValue(line)
  92. line_index = lines.index(line)
  93. # In the beginning we may have 2 M73 lines, but it makes logic less complicated
  94. if output_time:
  95. self.outputTime(lines, line_index, total_time, output_time_method)
  96. if output_percentage:
  97. # Emit 0 percent to sure Marlin knows we are overriding the completion percentage
  98. lines.insert(line_index, "M73 P0")
  99. elif line.startswith(";TIME_ELAPSED:"):
  100. # We've found one of the time elapsed values which are added at the end of layers
  101. # If we have seen this line before then skip processing it. We can see lines multiple times because we are adding
  102. # intermediate percentages before the line being processed. This can cause the current line to shift back and be
  103. # encountered more than once
  104. if line in line_set:
  105. continue
  106. line_set[line] = True
  107. # If total_time was not already found then noop
  108. if total_time == -1:
  109. continue
  110. current_time = self.getTimeValue(line)
  111. line_index = lines.index(line)
  112. if output_time:
  113. if output_frequency == 0:
  114. # Here we calculate remaining time
  115. self.outputTime(lines, line_index, total_time - current_time, output_time_method)
  116. else:
  117. # Here we calculate remaining time and how many outputs are expected for the layer
  118. layer_time_delta = int(current_time - previous_layer_end_time)
  119. layer_step_delta = int((current_time - previous_layer_end_time) / output_frequency)
  120. # If this layer represents less than 1 step then we don't need to emit anything, continue to the next layer
  121. if layer_step_delta != 0:
  122. # Grab the index of the current line and figure out how many lines represent one second
  123. step = line_index / layer_time_delta
  124. # Move new lines further as we add new lines above it
  125. lines_added = 1
  126. # Run through layer in seconds
  127. for seconds in range(1, layer_time_delta + 1):
  128. # Time from start to decide when to update
  129. line_time = int(previous_layer_end_time + seconds)
  130. # Output every X seconds and after last layer
  131. if line_time % output_frequency == 0 or line_time == total_time:
  132. # Line to add the output
  133. time_line_index = int((seconds * step) + lines_added)
  134. # Insert remaining time into the GCODE
  135. self.outputTime(lines, time_line_index, total_time - line_time, output_time_method)
  136. # Next line will be again lower
  137. lines_added = lines_added + 1
  138. previous_layer_end_time = int(current_time)
  139. if output_percentage:
  140. # Calculate percentage value this layer ends at
  141. layer_end_percentage = int((current_time / total_time) * 100)
  142. # Figure out how many percent of the total time is spent in this layer
  143. layer_percentage_delta = layer_end_percentage - previous_layer_end_percentage
  144. # If this layer represents less than 1 percent then we don't need to emit anything, continue to the next layer
  145. if layer_percentage_delta != 0:
  146. # Grab the index of the current line and figure out how many lines represent one percent
  147. step = line_index / layer_percentage_delta
  148. for percentage in range(1, layer_percentage_delta + 1):
  149. # We add the percentage value here as while processing prior lines we will have inserted
  150. # percentage lines before the current one. Failing to do this will upset the spacing
  151. percentage_line_index = int((percentage * step) + percentage)
  152. # Due to integer truncation of the total time value in the gcode the percentage we
  153. # calculate may slightly exceed 100, as that is not valid we cap the value here
  154. output = min(percentage + previous_layer_end_percentage, 100)
  155. # Now insert the sanitized percentage into the GCODE
  156. lines.insert(percentage_line_index, "M73 P{}".format(output))
  157. previous_layer_end_percentage = layer_end_percentage
  158. # Join up the lines for this layer again and store them in the data array
  159. data[layer_index] = "\n".join(lines)
  160. return data