chiark / gitweb /
TweakAtZ 4.0
[cura.git] / plugins / TweakAtZ.py
1 #Name: Tweak At Z 4.0
2 #Info: Change printing parameters at a given height
3 #Help: TweakAtZ
4 #Depend: GCode
5 #Type: postprocess
6 #Param: targetZ(float:5.0) Z height to tweak at (mm)
7 #Param: targetL(int:) (ALT) Layer no. to tweak at
8 #Param: twLayers(int:1) No. of layers used for change
9 #Param: behavior(list:Tweak value and keep it for the rest,Tweak value for single layer only) Tweak behavior
10 #Param: speed(int:) New TOTAL Speed (%)
11 #Param: printspeed(int:) New PRINT Speed (%)
12 #Param: flowrate(int:) New General Flow Rate (%)
13 #Param: flowrateOne(int:) New Flow Rate Extruder 1 (%)
14 #Param: flowrateTwo(int:) New Flow Rate Extruder 2 (%)
15 #Param: platformTemp(int:) New Bed Temp (deg C)
16 #Param: extruderOne(int:) New Extruder 1 Temp (deg C)
17 #Param: extruderTwo(int:) New Extruder 2 Temp (deg C)
18 #Param: fanSpeed(int:) New Fan Speed (0-255 PWM)
19
20 ## Written by Steven Morlock, smorloc@gmail.com
21 ## Modified by Ricardo Gomez, ricardoga@otulook.com, to add Bed Temperature and make it work with Cura_13.06.04+
22 ## Modified by Stefan Heule, Dim3nsioneer@gmx.ch since V3.0 (see changelog below)
23 ## This script is licensed under the Creative Commons - Attribution - Share Alike (CC BY-SA) terms
24
25 ## Uses -
26 ## M220 S<factor in percent> - set speed factor override percentage
27 ## M221 S<factor in percent> - set flow factor override percentage
28 ## M221 S<factor in percent> T<0-#toolheads> - set flow factor override percentage for single extruder
29 ## M104 S<temp> T<0-#toolheads> - set extruder <T> to target temperature <S>
30 ## M140 S<temp> - set bed target temperature
31 ## M106 S<PWM> - set fan speed to target speed <S>
32 ## M605/606 to save and recall material settings on the UM2
33
34 ##history / changelog:
35 ##V3.0.1: TweakAtZ-state default 1 (i.e. the plugin works without any TweakAtZ comment)
36 ##V3.1:   Recognizes UltiGCode and deactivates value reset, fan speed added, alternatively layer no. to tweak at,
37 ##        extruder three temperature disabled by '#Ex3'
38 ##V3.1.1: Bugfix reset flow rate
39 ##V3.1.2: Bugfix disable TweakAtZ on Cool Head Lift
40 ##V3.2:   Flow rate for specific extruder added (only for 2 extruders), bugfix parser,
41 ##        added speed reset at the end of the print
42 ##V4.0:   Progress bar, tweaking over multiple layers, M605&M606 implemented, reset after one layer option,
43 ##        extruder three code removed, tweaking print speed, save call of Publisher class,
44 ##        uses previous value from other plugins also on UltiGCode
45
46 version = '4.0'
47
48 import re
49 import wx
50 import time
51 try:
52         #MacOS release currently lacks some wx components, like the Publisher.
53         from wx.lib.pubsub import Publisher
54 except:
55         Publisher = None
56
57 def getValue(line, key, default = None):
58         if not key in line or (';' in line and line.find(key) > line.find(';') and
59                                                            not ";TweakAtZ" in key and not ";LAYER:" in key):
60                 return default
61         subPart = line[line.find(key) + len(key):] #allows for string lengths larger than 1
62         if ";TweakAtZ" in key:
63                 m = re.search('^[0-4]', subPart)
64         elif ";LAYER:" in key:
65                 m = re.search('^[+-]?[0-9]*', subPart)
66         else:
67                 #the minus at the beginning allows for negative values, e.g. for delta printers
68                 m = re.search('^[-]?[0-9]+\.?[0-9]*', subPart)
69         if m == None:
70                 return default
71         try:
72                 return float(m.group(0))
73         except:
74                 return default
75
76 with open(filename, "r") as f:
77         lines = f.readlines()
78 #Check which tweaks should apply
79 TweakProp = {'speed': speed is not None and speed != '',
80                          'flowrate': flowrate is not None and flowrate != '',
81                          'flowrateOne': flowrateOne is not None and flowrateOne != '',
82                          'flowrateTwo': flowrateTwo is not None and flowrateTwo != '',
83                          'platformTemp': platformTemp is not None and platformTemp != '',
84                          'extruderOne': extruderOne is not None and extruderOne != '',
85                          'extruderTwo': extruderTwo is not None and extruderTwo != '',
86                          'fanSpeed': fanSpeed is not None and fanSpeed != ''}
87 TweakPrintSpeed = printspeed is not None and printspeed != ''
88 TweakStrings = {'speed': "M220 S%f\n",
89                                 'flowrate': "M221 S%f\n",
90                                 'flowrateOne': "M221 T0 S%f\n",
91                                 'flowrateTwo': "M221 T1 S%f\n",
92                                 'platformTemp': "M140 S%f\n",
93                                 'extruderOne': "M104 S%f T0\n",
94                                 'extruderTwo': "M104 S%f T1\n",
95                                 'fanSpeed': "M106 S%d\n"}
96 target_values = {}
97 for key in TweakProp:
98         target_values[key]=eval(key)
99 old = {'speed': -1, 'flowrate': -1, 'flowrateOne': -1, 'flowrateTwo': -1, 'platformTemp': -1, 'extruderOne': -1,
100            'extruderTwo': -1, 'fanSpeed': -1, 'state': -1}
101 try:
102         twLayers = max(int(twLayers),1) #for the case someone entered something as 'funny' as -1
103 except:
104         twLayers = 1
105 pres_ext = 0
106 done_layers = 0
107 z = 0
108 x = None
109 y = None
110 layer = -100000 #layer no. may be negative (raft) but never that low
111 # state 0: deactivated, state 1: activated, state 2: active, but below z,
112 # state 3: active and partially executed (multi layer), state 4: active and passed z
113 state = 1
114 # IsUM2: Used for reset of values (ok for Marlin/Sprinter),
115 # has to be set to 1 for UltiGCode (work-around for missing default values)
116 IsUM2 = False
117 lastpercentage = 0
118 i = 0
119 l = len(lines)
120 oldValueUnknown = False
121 TWinstances = 0
122
123
124 try:
125         targetL_i = int(targetL)
126         targetZ = 100000
127 except:
128         targetL_i = -100000
129
130 if Publisher is not None:
131         if targetL_i > -100000:
132                 wx.CallAfter(Publisher().sendMessage, "pluginupdate",
133                                          "OpenPluginProgressWindow;TweakAtZ;Tweak At Z plugin is executed at layer " + str(targetL_i))
134         else:
135                 wx.CallAfter(Publisher().sendMessage, "pluginupdate",
136                                          "OpenPluginProgressWindow;TweakAtZ;Tweak At Z plugin is executed at height " + str(targetZ) + "mm")
137 with open(filename, "w") as file:
138         for line in lines:
139                 if int(i*100/l) > lastpercentage and Publisher is not None: #progressbar
140                         lastpercentage = int(i*100/l)
141                         wx.CallAfter(Publisher().sendMessage, "pluginupdate", "Progress;" + str(lastpercentage))
142                 if ';Layer count:' in line:
143                         TWinstances += 1
144                         file.write(';TweakAtZ instances: %d\n' % TWinstances)
145                 if not ('M84' in line or 'M25' in line or ('G1' in line and TweakPrintSpeed and state==3) or
146                                                 ';TweakAtZ instances:' in line):
147                         file.write(line)
148                 IsUM2 = ('FLAVOR:UltiGCode' in line) or IsUM2 #Flavor is UltiGCode!
149                 if ';TweakAtZ-state' in line: #checks for state change comment
150                         state = getValue(line, ';TweakAtZ-state', state)
151                 if ';TweakAtZ instances:' in line:
152                         try:
153                                 tempTWi = int(line[20:])
154                         except:
155                                 tempTWi = TWinstances
156                         TWinstances = tempTWi
157                 if ';Small layer' in line: #checks for begin of Cool Head Lift
158                         old['state'] = state
159                         state = 0
160                 #if ('G4' in line) and old['state'] > -1:
161                 #old['state'] = -1
162                 if ';LAYER:' in line: #new layer no. found
163                         layer = getValue(line, ';LAYER:', layer)
164                         if targetL_i > -100000: #target selected by layer no.
165                                 if state == 2 and layer >= targetL_i: #determine targetZ from layer no.
166                                         targetZ = z + 0.001
167                 if (getValue(line, 'T', None) is not None) and (getValue(line, 'M', None) is None): #looking for single T-cmd
168                         pres_ext = getValue(line, 'T', pres_ext)
169                 if 'M190' in line or 'M140' in line and state < 3: #looking for bed temp, stops after target z is passed
170                         old['platformTemp'] = getValue(line, 'S', old['platformTemp'])
171                 if 'M109' in line or 'M104' in line and state < 3: #looking for extruder temp, stops after target z is passed
172                         if getValue(line, 'T', pres_ext) == 0:
173                                 old['extruderOne'] = getValue(line, 'S', old['extruderOne'])
174                         elif getValue(line, 'T', pres_ext) == 1:
175                                 old['extruderTwo'] = getValue(line, 'S', old['extruderTwo'])
176                 if 'M107' in line: #fan is stopped; is always updated in order not to miss switch off for next object
177                         old['fanSpeed'] = 0
178                 if 'M106' in line and state < 3: #looking for fan speed
179                         old['fanSpeed'] = getValue(line, 'S', old['fanSpeed'])
180                 if 'M221' in line and state < 3: #looking for flow rate
181                         tmp_extruder = getValue(line,'T',None)
182                         if tmp_extruder == None: #check if extruder is specified
183                                 old['flowrate'] = getValue(line, 'S', old['flowrate'])
184                         elif tmp_extruder == 0: #first extruder
185                                 old['flowrateOne'] = getValue(line, 'S', old['flowrateOne'])
186                         elif tmp_extruder == 1: #second extruder
187                                 old['flowrateOne'] = getValue(line, 'S', old['flowrateOne'])
188                 if ('M84' in line or 'M25' in line):
189                         if state>0 and speed is not None and speed != '': #'finish' commands for UM Original and UM2
190                                 file.write("M220 S100 ; speed reset to 100% at the end of print\n")
191                                 file.write("M117                     \n")
192                         file.write(line)
193                 if 'G1' in line or 'G0' in line:
194                         newZ = getValue(line, 'Z', z)
195                         x = getValue(line, 'X', None)
196                         y = getValue(line, 'Y', None)
197                         e = getValue(line, 'E', None)
198                         f = getValue(line, 'F', None)
199                         if TweakPrintSpeed and state==3:
200                                 # check for pure print movement in target range:
201                                 if 'G1' in line and x != None and y != None and f != None and e != None and newZ==z:
202                                         file.write("G1 F%d X%1.3f Y%1.3f E%1.5f\n" % (int(f/100.0*float(printspeed)),getValue(line,'X'),
203                                                                                                                                   getValue(line,'Y'),getValue(line,'E')))
204                         else: #G1 command but not a print movement
205                                 file.write(line)
206                         # no tweaking on retraction hops which have no x and y coordinate:
207                         if (newZ != z) and (x is not None) and (y is not None):
208                                 z = newZ
209                                 if z < targetZ and state == 1:
210                                         state = 2
211                                 if z >= targetZ and state == 2:
212                                         state = 3
213                                         done_layers = 0
214                                         for key in TweakProp:
215                                                 if TweakProp[key] and old[key]==-1: #old value is not known
216                                                         oldValueUnknown = True
217                                         if oldValueUnknown: #the tweaking has to happen within one layer
218                                                 twLayers = 1
219                                                 if IsUM2: #Parameters have to be stored in the printer (UltiGCode=UM2)
220                                                         file.write("M605 S%d;stores parameters before tweaking\n" % (TWinstances-1))
221                                         if behavior == 1: #single layer tweak only and then reset
222                                                 twLayers = 1
223                                         if TweakPrintSpeed and behavior == 0:
224                                                 twLayers = done_layers + 1
225                                 if state==3:
226                                         if twLayers-done_layers>0: #still layers to go?
227                                                 if targetL_i > -100000:
228                                                         file.write(";TweakAtZ V%s: executed at Layer %d\n" % (version,layer))
229                                                         file.write("M117 Printing... tw@L%4d\n" % layer)
230                                                 else:
231                                                         file.write(";TweakAtZ V%s: executed at %1.2f mm\n" % (version,z))
232                                                         file.write("M117 Printing... tw@%5.1f\n" % z)
233                                                 for key in TweakProp:
234                                                         if TweakProp[key]:
235                                                                 file.write(TweakStrings[key] %
236                                                                                    float(old[key]+(float(target_values[key])-
237                                                                                                                    float(old[key]))/float(twLayers)*float(done_layers+1)))
238                                                 done_layers += 1
239                                         else:
240                                                 state = 4
241                                                 if behavior == 1: #reset values after one layer
242                                                         if targetL_i > -100000:
243                                                                 file.write(";TweakAtZ V%s: reset on Layer %d\n" % (version,layer))
244                                                         else:
245                                                                 file.write(";TweakAtZ V%s: reset at %1.2f mm\n" % (version,z))
246                                                         if not oldValueUnknown:
247                                                                 if not IsUM2:#executes only for UM Original and UM2 with RepRap flavor
248                                                                         for key in TweakProp:
249                                                                                 if TweakProp[key]:
250                                                                                         file.write(TweakStrings[key] % float(old[key]))
251                                                                 else: #executes on UM2 with Ultigcode
252                                                                         file.write("M606 S%d;recalls saved settings\n" % (TWinstances-1))
253                                 # re-activates the plugin if executed by pre-print G-command, resets settings:
254                                 if z < targetZ and state >= 3:
255                                         state = 2
256                                         done_layers = 0
257                                         if targetL_i > -100000:
258                                                 file.write(";TweakAtZ V%s: reset below Layer %d\n" % (version,targetL_i))
259                                         else:
260                                                 file.write(";TweakAtZ V%s: reset below %1.2f mm\n" % (version,targetZ))
261                                         if not IsUM2: #executes only for UM Original and UM2 with RepRap flavor
262                                                 for key in TweakProp:
263                                                         if TweakProp[key]:
264                                                                 file.write(TweakStrings[key] % float(old[key]))
265                                         else:
266                                                 file.write("M606 ;recalls saved settings\n")
267                 i+=1
268 if Publisher is not None:
269         wx.CallAfter(Publisher().sendMessage, "pluginupdate", "Progress;100")
270         time.sleep(1)
271         wx.CallAfter(Publisher().sendMessage, "pluginupdate", "ClosePluginProgressWindow")